<template>
  <div
    ref="container"
    class="grid-layout-design"
    :style="{
      minHeight: `${dataStore.layoutData.height}px`,
    }"
    @dragover="onDragOver"
    @drop.stop="onDrop"
    @click="onLayoutContentClick"
  >
    <grid-layout
      ref="gridLayout"
      :layout="gridList"
      :col-num="dataStore.gridColNum"
      :row-height="dataStore.gridRowHeight"
      :is-draggable="true"
      :is-resizable="true"
      :is-mirrored="false"
      :vertical-compact="false"
      :prevent-collision="true"
      :margin="dataStore.girdMargin"
      :use-css-transforms="true"
      @layout-ready="layoutReady"
      @layout-updated="layoutUpdatedEvent"
    >
      <template v-for="(item, index) in dataStore.gridList">
        <div :key="item.i" :class="`box-item-${item.i}`">
          <grid-item
            v-if="item.chartType !== touchGridTypeName"
            :id="`grid-item-${item.i}`"
            ref="gridItems"
            :key="item.i"
            :index="index"
            :class="`grid-item grid-item-${item.i} ${
              filterAssociationChartHover.includes(item.widgetId) ||
              currentFilterFormAsCharts.includes(item.widgetId)
                ? 'active'
                : ''
            }`"
            :style="getGridStyle(item)"
            :scale="dataStore.scaleValue"
            drag-allow-from=".grid-item-drag-handler"
            :is-draggable="item.draggable"
            :x="item.x"
            :y="item.y"
            :w="item.w"
            :h="item.h"
            :i="item.i"
            :min-w="item.minW"
            :min-h="4"
            @cornermouseup="onGridCornerMouseup"
            @resized="
              () => {
                onGridItemResized(item, index)
              }
            "
            @moved="
              () => {
                onGridItemMoved(item, index)
              }
            "
            @dragstart="
              (grid) => {
                onGridItemDragStart()
              }
            "
            @dragmove="
              (grid) => {
                onGridItemDragMove(grid)
              }
            "
            @dragend="
              (grid) => {
                onGridItemDragEnd()
              }
            "
            @resizemove="
              (grid) => {
                onGridItemResizeMove(grid)
              }
            "
          >
            <div class="grid-item-drag-handler"></div>
            <div v-if="item.isLoading" class="loading-status">
              <a-icon class="sync-icon" type="sync" spin /><span
                class="loading-text"
                >加载中...</span
              >
            </div>

            <div class="drag-marker" :class="{ 'is-loading': item.isLoading }">
              <div
                v-if="dataStore.SettingMode === 'interaction'"
                class="id-box"
              >
                ID:{{ item.i }}
              </div>
              <!-- <template v-if="isEditShow(item)">
                <a-tooltip placement="topLeft" title="添加|编辑控件">
                  <div class="grid-item-edit" @click="editChart(item, index)">
                    <img class="chart-icon-edit" src="~@/assets/edit.png" />
                  </div>
                </a-tooltip>
              </template> -->
              <a-tooltip
                v-if="item.chartType === 'configuration'"
                placement="top"
                :title="
                  configurationExecuteId !== item.widgetId ? '执行' : '停止'
                "
              >
                <a-icon-font
                  v-if="configurationExecuteId !== item.widgetId"
                  type="iconkaishi"
                  :class="{
                    'grid-item-execute': true,
                    disable: configurationExecuteId,
                  }"
                  @click.stop="saveConfigurationParams(item)"
                />
                <a-icon-font
                  v-else
                  type="icontingzhi"
                  class="grid-item-execute"
                  @click.stop="stopConfigurationExecute(item)"
                />
              </a-tooltip>
              <!-- filterForm 工具栏  -->
              <filter-form-toolbar
                v-if="checkIsFilterForm(item) && isFilterEnabled(item)"
                :grid-item="item"
                :index="index"
                :disabled="isFilterFormRunning"
                :is-running="checkIsRunning(item)"
                @start="
                  () => {
                    filterFormExecute(true, item, index)
                  }
                "
                @stop="
                  () => {
                    filterFormExecute(false, item, index)
                  }
                "
                @reset="
                  () => {
                    filterFormReset(item)
                  }
                "
              />
              <a-tooltip placement="top" title="点击删除">
                <div
                  class="grid-item-close"
                  @click.stop="deleteChart(item, index)"
                >
                  <a-icon-font type="iconicon-beifen5" />
                </div>
              </a-tooltip>
            </div>
            <div
              v-if="
                item.widgetId === configurationExecuteId || checkIsRunning(item)
              "
              class="progress-bar"
            ></div>
            <!-- 标题 -->
            <div
              v-if="
                !item.showChart &&
                item.chartOptions &&
                item.chartType !== 'filter'
              "
              class="title-box"
              :class="{
                'no-title': [undefined, null, ''].includes(
                  item.chartOptions.title
                ),
              }"
              @click.stop="gridItemClick(item)"
            >
              {{ item.chartOptions ? item.chartOptions.title || '' : '' }}
            </div>
            <!-- item.chartOptions.value && (item.chartOptions.value.length === 0) -->
            <div
              v-if="
                !item.isLoading &&
                item.chartOptions &&
                checkIsNoData(item) &&
                !['text', CONFIGURATION_NAME].includes(item.chartType)
              "
              class="no-data"
              @click.stop="gridItemClick(item)"
            >
              <div class="no-data-block">
                <img
                  v-if="item.showChart"
                  class="no-data-img"
                  alt="no-data"
                  src="~@/assets/no-data.png"
                />
                <img
                  v-else
                  class="no-data-img"
                  alt="empty"
                  :src="
                    `${publicPath}${getChartImage(item.chartType)}` ||
                    '@/assets/view/empty.png'
                  "
                />
                <span class="no-data-text">{{
                  errorFilterWidgets.includes(item.i)
                    ? errorMsgMap[item.i]
                    : '暂无数据'
                }}</span>
              </div>
            </div>
            <div
              :style="getBoxHeightStyle(item)"
              @click.stop="gridItemClick(item)"
            >
              <div
                v-if="item.chartType === filterTypeName"
                :ref="`control-list-${item.i}`"
                class="control-box-list"
                :class="'list-' + item.layout"
              >
                <controlVue
                  v-for="(controlConfig, j) in item.fieldConfig"
                  :key="j"
                  v-model="controlConfig.value"
                  :grid-item="item"
                  :field-config="controlConfig"
                  :layout="item.layout"
                  :control-type="controlConfig.controlRender"
                  :prop="{ size: 'default' }"
                  :label="controlConfig.name"
                  :options="controlConfig.options"
                ></controlVue>
              </div>
              <pageTableVue
                v-else-if="item.chartType === tableTypeName"
                :id="`grid-${item.i}`"
                :ref="`chart-${item.i}`"
                :is-sortable="true"
                :page-disabled="false"
                :config="item"
                @pagechange="
                  (pageConfig) => onTablePageChange(item, pageConfig)
                "
                @sortchange="
                  (sortData) => {
                    onTableSortchange(item, index, sortData)
                  }
                "
              >
              </pageTableVue>
              <text-editor
                v-else-if="item.chartType === 'text'"
                :id="`box-item-${item.i}`"
                class="text-editor"
                :text-value="item.chartOptions.value"
                @render-complete="removeLoading(index)"
                @handle-click="gridItemClick(item, index)"
                @highlight-formula="highlightFormula"
                @editor-blur="handleTextBlur(index, item)"
                @add-error-border="addErrorBorder(item.i)"
                @remove-error-border="removeErrorBorder(item.i)"
                @text-change="
                  (val) => {
                    handleTextChange(index, val)
                  }
                "
              ></text-editor>
              <operator-configuration
                v-else-if="item.chartType === 'configuration'"
                :config="item.chartOptions"
                :disabled="configurationExecuteId === item.widgetId"
              />
              <!-- 筛选器控件 -->
              <div
                v-else-if="checkIsFilterForm(item)"
                class="filter-form-hander"
                @mouseenter="
                  () => {
                    handleFilterFormMouseEnter(item)
                  }
                "
                @mouseleave="
                  () => {
                    handleFilterFormMouseLeave(item)
                  }
                "
              >
                <filter-form
                  v-if="!pipelineId && inited? true : dataset && dataset.length > 0 && pipelineId"
                  :ref="`filter-form-${item.i}`"
                  :config="item"
                  :dataset="dataset"
                  :pipeline-id="pipelineId"
                  :node-id-map="{}"
                  :disabled="isFilterFormRunning"
                  @change="
                    (dataSet) => {
                      filterChange(dataSet, item)
                    }
                  "
                >
                </filter-form>
              </div>
              <template v-else>
                <Chart
                  v-if="item.widgetId || gridItemIsGeoMap(item)"
                  :id="`grid-${item.i}`"
                  :ref="`chart-${item.i}`"
                  class="chart-design"
                  :config="item.chartOptions"
                  :chart-type="item.chartType"
                  @chartinit="
                    (instance) => {
                      onChartInstanceInit(instance, item)
                    }
                  "
                  @chartevent="
                    (data) => {
                      onChartEvent(data, item, index)
                    }
                  "
                >
                </Chart>
              </template>
            </div>
          </grid-item>
        </div>
      </template>
    </grid-layout>
    <dialogControllerSetting
      v-if="dialogControllerSettingConfig.visible"
      ref="dialog-contrller-setting"
      v-bind="dialogControllerSettingConfig"
      @handle="onDialogControllerSettingHandle"
    ></dialogControllerSetting>

    <!-- 吸附参考线 -->
    <template v-if="draggingDomRect && draggingItem">
      <div
        v-if="alignmentFlagHT"
        class="line ht"
        :style="{
          top: `${ht}px`,
          ...htStyle,
        }"
      ></div>
      <div
        v-if="alignmentFlagHC"
        class="line hc"
        :style="{
          top: `${hc}px`,
          ...hcStyle,
        }"
      ></div>
      <div
        v-if="alignmentFlagHB"
        class="line hb"
        :style="{
          top: `${hb}px`,
          ...hbStyle,
        }"
      ></div>
      <div
        v-if="alignmentFlagVL"
        class="line vl"
        :style="{
          left: `${vl}px`,
          ...vlStyle,
        }"
      ></div>
      <div
        v-if="alignmentFlagVC"
        class="line vc"
        :style="{
          left: `${vc}px`,
          ...vcStyle,
        }"
      ></div>
      <div
        v-if="alignmentFlagVR"
        class="line vr"
        :style="{
          left: `${vr}px`,
          ...vrStyle,
        }"
      ></div>
    </template>
  </div>
</template>
<script lang="ts">
import {
  getDashboardByProjectId,
  saveDashboard,
  updateDashboard,
} from '@/api/dashboard'
import { cloneDeep, isArray, isString } from 'lodash'
import { clipGraphData } from '@/util/util'
// import { tcolumnQueryDetail } from '@/api/visualization'
import {
  deleteWidget,
  getWidgetData,
  saveWidget,
  updateWidget,
  widgetQueryByProject,
  // getRelateWidgets,
} from '@/api/widget'
import {
  buildSaveParameters,
  checkFilterFormIsEnabled,
  checkIsFilterForm,
  filterGraphData,
  getFilterDataFromFilterForm,
  mergeFilterFormFilters,
} from '@/util/common'

// import { getWidgetJson } from '@/config/setting-chart'
import {
  // ChartDataTypeEnum,
  chartTypes,
  chartTypesSub,
  filterTypeName,
  filterFormTypeName,
  GeoMapDefaultConfig,
  GEOMAP_NAME,
  getChartIconImage,
  // getDefaultGraphConfig,
  tableTypeName,
  CONFIGURATION_NAME,
  chartDetailToVisClassMaps,
  GRAPH_ARRAY,
} from '@/config/contant'
import VisualizationStore from '@/store/modules/visualization'
import { Component, Vue, Watch } from 'vue-property-decorator'
import { publicPath } from '@/api/constants'
import { pipelineExecute, pipelineStop } from '@/api/pipeline'
import { simpleUpdate } from '@/api/task'
import LoadingMask from '@/components/loading-mask/loadingMask.vue'
import GridLayout from '@/components/vueGridLayout/components/GridLayout.vue'
import GridItem from '@/components/vueGridLayout/components/GridItem.vue'
import Chart from '@/components/chart/chart.vue'
import { getDetailDefaultOptionsByChartType } from '@/components/common/VisualizationComp/default-chart-options' // 默认配置
import OperatorConfiguration from '@/components/visualization/OperatorConfiguration.vue'
import pageTableVue from '../pageTable/pageTable.vue'
import TextEditor from '../textEditor/textEditor.vue'
import controlVue from '../control/control.vue'
import FilterForm from '@/components/filter-form/index.vue'
import FilterFormToolbar from '@/components/filter-form/filter-form-toolbar.vue'
import {
  ColumnInterface,
  componentTypeEnum,
  ControlConfigurationInterface,
  controlLayoutEnum,
  controlTypeEnum,
  GridItemDataInterface,
  gridItemFilterInterface,
  KeyValueStore,
} from '../vueGridLayout/interfate/grid-Item-data-inteface'
import {
  getControllerRenderByFormData,
  getDefaultValue,
  getDefaultValueFromDashBoardData,
  // getFormSettingByComponent,
} from './controller-setting'
import dialogControllerSetting from './dialog-controller-setting.vue'
import {
  parseChartOptionToFunction,
  parseGetDataResult,
  restructGetDataResult,
  getWidgetGraphData,
} from '../common/VisualizationDashboard/util'
import { enableConvertToCoor } from '@/util/recommend-visualization'
import { DomReact, HoverFilterFormInfo } from './util'
import { IKeyValueStore } from '@/store/modules/dataview'
import { message } from 'ant-design-vue'
import visGraphService from '@/store/modules/vis-graph-service'
import { queryDatalist2 } from '@/api/data-list'
// import visGraphService from '@/store/modules/vis-graph-service'
// import DataVisualization from '../studio/data/node-edit-panel/DataVisualization.vue'

const defaultChartoptions = {
  titleIsShow: true,
  // titleHeight: 44,
  titleTextMaxLength: 30,
  titleFontSize: 16,
  titleFontColor: '#373B52',
  xAxisIsShow: true,
  yAxisIsShow: true,
  maxBarWidth: 28,
  // legendPosition: 'right',
}

const touchGridTypeName = 'touch-grid-item'

@Component({
  components: {
    LoadingMask,
    GridLayout,
    GridItem,
    Chart,
    controlVue,
    pageTableVue,
    dialogControllerSetting,
    TextEditor,
    FilterForm,
    FilterFormToolbar,
    OperatorConfiguration,
  },
})
export default class VisualizationContent extends Vue {
  private publicPath = publicPath
  private configurationExecuteId: string = '' // 执行中的控件id，一次只允许一个控件执行
  private configurationExecuteSessionId: string = '' // 控件执行后的sessionId

  private filterFormExecuteId: string = '' // 执行中的筛选控件

  inited: boolean = false

  /** 鼠标active状态下的filter-form信息 */
  hoverFilterFormInfo: HoverFilterFormInfo | null = null

  draggingFlag: boolean = false // 有组件处于拖拽状态
  draggingItem: any = null // 被拖拽的对象
  alignmentFlagHT: boolean = false // 检测到有组件横向头部对齐标识
  alignmentFlagHC: boolean = false // 检测到有组件横向中部对齐标识
  alignmentFlagHB: boolean = false // 检测到有组件横向底部对齐标识
  alignmentFlagVL: boolean = false // 检测到有组件纵向左边对齐标识
  alignmentFlagVC: boolean = false // 检测到有组件纵向中部对齐标识
  alignmentFlagVR: boolean = false // 检测到有组件纵向右侧对齐标识
  ht: number = -1 // 横向头部参考线偏移量
  htStyle: IKeyValueStore = {} // 头部参考线样式
  hc: number = -1 // 横向中部参考线偏移量
  hcStyle: IKeyValueStore = {} // 横向中部参考线样式
  hb: number = -1 // 横向底部参考线偏移量
  hbStyle: IKeyValueStore = {} // 横向底部参考线样式
  vl: number = -1 // 纵向左侧参考线偏移量
  vlStyle: IKeyValueStore = {} // 纵向左侧参考线样式
  vc: number = -1 // 纵向中部参考线偏移量
  vcStyle: IKeyValueStore = {} // 纵向中部参考线样式
  vr: number = -1 // 纵向右侧参考线偏移量
  vrStyle: IKeyValueStore = {} // 纵向右侧参考线样式
  draggingDomRect: DomReact | null = null

  filterTypeName: string = filterTypeName
  filterFormTypeName: string = filterFormTypeName
  tableTypeName: string = tableTypeName
  CONFIGURATION_NAME = CONFIGURATION_NAME

  touchGridTypeName: string = touchGridTypeName

  dialogControllerSettingConfig: KeyValueStore = {
    visible: false,
    from: null,
    layout: controlLayoutEnum.vertical,
    title: '组件查询条件配置',
    width: '80vw',
    gridItem: null,
    height: 'calc(80vh - 100px)',
  }

  //  for text comp insert formula
  public get insertFormula() {
    return VisualizationStore.getInsertFormula
  }

  public get deleteFormula() {
    return VisualizationStore.getDeleteFormula
  }

  public get updateFormula() {
    return VisualizationStore.getUpdateFormula
  }

  public get formulaList() {
    return VisualizationStore.getFormulaList
  }

  @Watch('formulaList', { deep: true })
  handleFormulaListChange(newValue: any) {
    if (newValue) {
      this.$nextTick(() => {
        //  check 是否失效，是否结果有误 'UPDATED_FAILED', 'DELETED'
        newValue.forEach((list: any) => {
          list.formulaList.forEach((formula: any) => {
            if (['UPDATED_FAILED', 'DELETED'].includes(formula.status)) {
              this.modifyFormulaInText(formula, 'delete')
            } else {
              //  根据状态。更新result值
              this.modifyFormulaInText(formula, 'update')
            }
          })
        })
      })
    }
  }

  @Watch('insertFormula', { deep: true })
  public onInsertFormula(newValue: any) {
    if (newValue.formula) {
      this.detectInsertContent(newValue)
    }
  }

  @Watch('deleteFormula', { deep: true })
  public onDeleteFormula(newValue: any) {
    if (newValue.formula) {
      this.modifyFormulaInText(newValue, 'delete')
      this.saveAllTextValue()
    }
  }

  @Watch('updateFormula', { deep: true })
  public onUpdateFormula(newValue: any) {
    if (newValue.formula) {
      this.modifyFormulaInText(newValue, 'update')
      this.saveAllTextValue()
    }
  }

  @Watch('dataStore.activeSettingItem', { deep: true })
  public onDataStoreActiveSettingItemChange(newValue: string) {
    if (
      newValue === 'chart' &&
      this.dataStore.currentGridItem &&
      checkIsFilterForm(this.dataStore.currentGridItem)
    ) {
      this.dataStore.setCurrentFilterFormAsCharts(
        this.dataStore.currentGridItem.chartOptions?.associationChart || []
      )
    } else {
      this.dataStore.setCurrentFilterFormAsCharts([])
    }
  }

  /** 当前激活状态下的filter-chart的关联图表 */
  get currentFilterFormAsCharts(): Array<string | number> {
    return this.dataStore.currentFilterFormAsCharts || []
  }

  modifyFormulaInText(newValue: any, flag: string) {
    this.$nextTick(() => {
      setTimeout(() => {
        const elements = document.querySelectorAll('.mceNonEditable')
        let formula: any = ''
        let result: any = ''
        // eslint-disable-next-line no-restricted-syntax
        for (const item of elements) {
          const id = item.getAttribute('data-id')
          if (newValue.id === Number(id)) {
            //  可能拿的是新插入的formula, result
            formula = item.getAttribute('data-formula')
            result = item.getAttribute('data-result')
            if (formula !== newValue.formula) {
              //  防止取到新公式的情况
              break
            }
          }
        }

        if (formula.length > 0) {
          this.getTinymce().editors.forEach((editor: any) => {
            let content = cloneDeep(editor.getContent())
            content = content.replaceAll(
              `<a class="mceNonEditable formula-style" data-id="${newValue.id}" data-result="${result}" data-formula="${formula}">${formula}</a>`,
              flag === 'delete'
                ? `<a class="mceNonEditable error-style" data-id="${newValue.id}">ERROR</a>`
                : `<a class="mceNonEditable formula-style" data-id="${newValue.id}" data-result="${newValue.result}" data-formula="${newValue.formula}">${newValue.formula}</a>`
            )
            content = content.replaceAll(
              `<a class="mceNonEditable result-style" data-id="${newValue.id}" data-result="${result}" data-formula="${formula}">${result}</a>`,
              flag === 'delete'
                ? `<a class="mceNonEditable error-style" data-id="${newValue.id}">ERROR</a>`
                : `<a class="mceNonEditable result-style" data-id="${newValue.id}" data-result="${newValue.result}" data-formula="${newValue.formula}">${newValue.result}</a>`
            )
            if (flag === 'update') {
              content = content.replaceAll(
                `<a class="mceNonEditable error-style" data-id="${newValue.id}">ERROR</a>`,
                `<a class="mceNonEditable result-style" data-id="${newValue.id}" data-result="${newValue.result}" data-formula="${newValue.formula}">${newValue.result}</a>`
              )
            }
            editor.setContent(content)
          })
        }
      }, 1000)
    })
  }

  private getGlobal = (): any => window

  private getTinymce = () => {
    const global = this.getGlobal()

    return global && global.tinymce ? global.tinymce : null
  }

  public detectInsertContent(item: any) {
    this.getTinymce().activeEditor.execCommand(
      'mceInsertContent',
      false,
      ` <a class='formula-style mceNonEditable'
            data-id=${item.id}
            data-result=${item.result}
            data-formula='${item.formula}'
        >
            ${item.formula}
        </a> `
    )
  }

  public highlightFormula(id: number) {
    VisualizationStore.setHighlightFormula(id)
  }

  public async handleTextBlur(index: number, gridItem: any) {
    //  set draggable for the item
    this.$set(this.gridList[index], 'draggable', true)
    this.$nextTick(() => {
      VisualizationStore.setCurrentGridItem(gridItem)
    })
    //  Save text value by update Widget
    this.saveTextValue(gridItem)
  }

  private async saveTextValue(gridItem: any) {
    //  Save text value by update Widget
    gridItem.chartOptions.value = this.transferTextValue(
      gridItem.chartOptions.value
    )
    const parameters = buildSaveParameters(gridItem)
    if (this.dashBoardId) {
      parameters.data.dashboardId = this.dashBoardId
    }
    if (gridItem.widgetId) {
      await updateWidget(parameters) // 保存text widget
    }
  }

  private saveAllTextValue() {
    this.$nextTick(() => {
      setTimeout(() => {
        this.gridList.forEach((item: any) => {
          if (item.chartType === 'text') {
            this.saveTextValue(item)
          }
        })
      }, 1000)
    })
  }

  public addErrorBorder(flag: string) {
    const element = document.querySelector(`.grid-item-${flag}`)
    element?.classList.add('error-border')
  }

  public removeErrorBorder(flag: string) {
    const element = document.querySelector(`.grid-item-${flag}`)
    element?.classList.remove('error-border')
  }

  public removeLoading(index: number) {
    this.$set(this.gridList[index], 'isLoading', false)
  }

  public handleTextChange(index: number, value: string) {
    this.$set(this.gridList[index].chartOptions, 'value', value)
  }

  get dataStore() {
    return VisualizationStore
  }

  get gridList(): Array<GridItemDataInterface> {
    return VisualizationStore.gridList
  }

  get unitX() {
    return VisualizationStore.layoutData.width / this.dataStore.gridColNum
  }

  get unitY() {
    return this.dataStore.gridRowHeight + this.dataStore.girdMargin[1]
  }

  get projectId() {
    return (this.$route.params as any).id || localStorage.getItem('projectId')
  }

  mounted() {
    localStorage.setItem('projectId', `${(this.$route.params as any).id || ''}`)
    this.dataStore.setCurrentGridItem(null)
    this.dataStore.setGridList([])
    this.dataStore.setActiveSettingItem('background')
    this.dataStore.eventBus.$on('aschartchange', (data: any) => {
      this.onAschartchange(data)
    })
    // 由于pipeline编辑后表名称会有变化，所以初始化之前先查询对应数据信息
    this.datasetList()
    this.init()
    this.initConfigurationWS()
    ;(window as any).dataStore = this.dataStore
  }

  init() {
    VisualizationStore.setActiveSettingItem('background')
    VisualizationStore.setLoadingStatus(true)
    VisualizationStore.fetchFormulaList({
      projectId: Number(this.$route.params.id),
    })
    //  init dashboard width/height/background color; Case: switch different projects in one session
    VisualizationStore.setBackgroundWidth(1180)
    // VisualizationStore.setBackgroundHeight(1600)
    VisualizationStore.setBackgroundColor('#FFF')
    getDashboardByProjectId({
      data: {
        projectId: this.projectId,
      },
    })
      .then(async (response) => {
        VisualizationStore.setLoadingStatus(false)
        if (
          response.data.code === 100 &&
          response.data.result &&
          response.data.result.id
        ) {
          this.dashBoardId = response.data.result.id
          VisualizationStore.setCurrentDashboardData(response.data.result)
          const layout = response.data.result.layout
            ? JSON.parse(response.data.result.layout)
            : {}
          if (layout.w && layout.h) {
            VisualizationStore.setBackgroundWidth(Number.parseFloat(layout.w))
            VisualizationStore.setBackgroundHeight(Number.parseFloat(layout.h))
            VisualizationStore.setBackgroundColor(layout.background)
          }
          if (Array.isArray(layout.coordinations)) {
            VisualizationStore.setCoordination(layout.coordinations)
          }
          layout.gridItems = layout.gridItems || []
          let widgetList: any = []
          const { pipelineId } = response.data.result
          this.dataStore.setPipelineId(pipelineId)
          if (
            layout.gridItems.some(
              (item: any) => item.chartType === CONFIGURATION_NAME
            ) &&
            pipelineId
          ) {
            widgetList = await this.queryNewPipelineWidgetList(pipelineId)
          }
          setTimeout(() => {
            if (layout.gridItems && Array.isArray(layout.gridItems)) {
              const ids: Array<number> = []
              layout.gridItems.forEach(
                (gridItem: KeyValueStore, index: number) => {
                  if (
                    ![CONFIGURATION_NAME, 'text'].includes(gridItem.chartType)
                  ) {
                    gridItem.chartType = gridItem.chartType.split
                      ? gridItem.chartType.split('|')
                      : gridItem.chartType
                  }
                  // 处理graph的情况
                  // const isGraph = false
                  if (gridItem.config.length !== 0) {
                    const config = JSON.parse(gridItem.config)
                    if (gridItem.chartType === 'configuration') {
                      // 控件组件要获取新的pipeline下面的参数配置的值
                      // 同时需要更新childId 的情况 - info
                      const target = widgetList
                        .filter(
                          (item: { tid: number; widgets: any[] }) =>
                            item.tid === config.chartOptions.dataId
                        )[0]
                        ?.widgets.filter(
                          (item: KeyValueStore) => item.type === 'configuration'
                        )[0]
                      if (target && target.data.params) {
                        const newValue = target.data.params.reduce(
                          (accu: any, current: any) => {
                            accu[current.name] = current.value
                            return accu
                          },
                          {}
                        )
                        config.chartOptions.params.forEach((p: any) => {
                          p.value = newValue[p.name]
                        })
                        config.chartOptions.value = newValue
                      }
                      if (target && target.data.info) {
                        config.chartOptions.info = target.data.info
                      }
                    }

                    gridItem.dataQuery = {
                      data: {
                        id: config.chartOptions.dataId,
                        type: config.chartOptions.dataType,
                        // type: config.formData.dataType_
                        //   ? config.formData.dataType_
                        //   : config.formData.dataType
                        //   ? config.formData.dataType
                        //   : 'dataset',
                        widgetJson: config.widgetJson,
                      },
                    }
                    if (config.widgetJson) {
                      console.log('config.widgetJson', config.widgetJson)
                    }
                    if (
                      config?.interactionJson?.relateWidgets &&
                      config?.interactionJson?.relateWidgets.length > 0
                    ) {
                      gridItem.interactionJson = config.interactionJson
                    }
                    gridItem.chartOptions = config.chartOptions
                    gridItem.chartOptions.interactions = [
                      { type: 'element-single-selected' },
                    ]
                    // gridItem.chartOptions.xAxisLabelMaxLength = 12
                    // gridItem.chartOptions.yAxisLabelMaxLength = 12
                    // gridItem.x = config.x
                    // gridItem.y = config.y
                    // gridItem.w = config.w
                    // gridItem.h = config.h
                    // gridItem.i = config.i
                  }
                  if (gridItem.widgetId) {
                    ids.push(gridItem.widgetId)
                  } else if (!gridItem.chartOptions) {
                    layout.gridItems[index].chartOptions = {
                      showChart: false,
                      value: [],
                      size: [
                        Math.floor(
                          this.unitX * this.dataStore.defaultGridWidth
                        ) - 20,
                        Math.floor(
                          this.unitY * this.dataStore.defaultGridHeight
                        ) + 50,
                      ],
                    }
                  }
                }
              )
              const gridItems = (layout.gridItems as Array<any>) || []
              if (
                layout.filters &&
                isArray(layout.filters) &&
                layout.filters.length > 0
              ) {
                ;(layout.filters as Array<KeyValueStore>).forEach(
                  (filterItem) => {
                    const fieldConfig: Array<KeyValueStore> = []
                    filterItem.fieldConfig.forEach(
                      (fieldInfo: KeyValueStore) => {
                        const column: ColumnInterface = {
                          tableId: filterItem.dataId,
                          label: fieldInfo.column,
                          value: fieldInfo.column,
                          type: fieldInfo.columnType,
                          isDate:
                            controlTypeEnum.timePicker ===
                            fieldInfo.controlType,
                          isNumber: false, // 暂时不支持数值型
                        }
                        fieldConfig.push({
                          ...fieldInfo,
                          column,
                          componentId:
                            (fieldInfo.associationChart as Array<string>)[0] ||
                            '',
                          value: getDefaultValueFromDashBoardData(
                            fieldInfo.controlRender,
                            fieldInfo
                          ), // fieldInfo.defaultValue
                        })
                      }
                    )
                    gridItems.push({
                      ...filterItem,
                      fieldConfig,
                    })
                  }
                )
              }
              if (
                layout.tables &&
                isArray(layout.tables) &&
                layout.tables.length > 0
              ) {
                gridItems.push(...layout.tables)
              }
              // if (
              //   layout.text &&
              //   isArray(layout.text) &&
              //   layout.text.length > 0
              // ) {
              //   gridItems.push(...layout.text)
              // }
              VisualizationStore.setGridList(
                this.sortLayoutItemsByRowCol(gridItems)
              )
              //  bug fix for enter scroll issue
              this.$nextTick(() => {
                const element: any = document.querySelectorAll('.text-editor')
                element.forEach((item: any) => {
                  item.parentNode.style.height = 'calc(100% - 38px)'
                })
                this.saveAllTextValue()
              })
              this.initWidget()
            }
          }, 100)
        } else {
          VisualizationStore.setCurrentDashboardData({}) // 当前没有dashboard
        }
      })
      .catch(() => {
        VisualizationStore.setLoadingStatus(false)
        VisualizationStore.setCurrentDashboardData({}) // 当前没有dashboard
      })
  }
  sortLayoutItemsByRowCol(layout: Array<any>): Array<any> {
    return layout.sort((a, b) => {
      if (a.y > b.y || (a.y === b.y && a.x > b.x)) {
        return 1
      }
      return -1
    })
  }
  /**
   * 删除touchGridItem
   */
  removeTouch() {
    const findIndex = this.gridList.findIndex(
      (item) => item.chartType === touchGridTypeName
    )
    if (findIndex >= 0) {
      VisualizationStore.removeGridItem(findIndex)
    }
  }

  /** 判断gridItem是否为地图组件 */
  gridItemIsGeoMap(gridItem: GridItemDataInterface) {
    return (gridItem.chartType as Array<string>).includes(GEOMAP_NAME)
  }

  /** 检测是否是筛选器控件 */
  gritItemIsFilterForm(gridItem: GridItemDataInterface) {
    return (
      gridItem.chartType === filterFormTypeName ||
      gridItem.chartType[0] === filterFormTypeName
    )
  }

  /**
   * 由于地图组件特殊,value 不是数组
   */
  checkIsNoData(gridItem: gridItemFilterInterface) {
    if (this.gridItemIsGeoMap(gridItem)) {
      return false
    }
    if (this.gritItemIsFilterForm(gridItem)) {
      return false
    }
    const chartType = Array.isArray(gridItem.chartType)
      ? gridItem.chartType[1]
      : gridItem.chartType
    if (GRAPH_ARRAY.includes(chartType)) {
      const { nodes } = gridItem.chartOptions.value || {}
      return !(nodes && Object.keys(nodes).length > 0)
    }
    if (isArray(gridItem.chartOptions.value)) {
      return gridItem.chartOptions.value.length === 0
    }
    return false
  }

  /**
   * touch GridItem
   */
  touchGridList() {
    const touchGridItem: GridItemDataInterface = {
      chartType: touchGridTypeName,
      x: 0,
      y: VisualizationStore.layoutData.height / this.unitY + 10,
      w: 1,
      h: 1,
      minW: 0,
      minH: 0,
      chartOptions: {},
      i: touchGridTypeName,
    }
    VisualizationStore.addGridItem(touchGridItem)
    this.$nextTick(() => {
      this.removeTouch()
    })
  }
  /**
   * 初始化wiget
   */
  initWidget() {
    this.gridList.forEach((gridItem: GridItemDataInterface, index: number) => {
      if (gridItem.chartType && gridItem.chartType === CONFIGURATION_NAME) {
        return
      }

      if (checkIsFilterForm(gridItem)) {
        return
      }
      parseChartOptionToFunction(gridItem.chartOptions)
      if (
        (gridItem.widgetId ||
          (gridItem.chartType === 'table' &&
            gridItem.chartOptions.dataId &&
            isArray(gridItem.chartOptions.columns) &&
            gridItem.chartOptions.columns.length > 0)) &&
        gridItem.chartOptions
      ) {
        this.getWidgetData(
          gridItem.chartOptions as KeyValueStore,
          gridItem,
          index
        )
      }
      // else if (gridItem.chartType === 'text') {
      //   this.$set(gridItem, 'isLoading', true)
      // }
    })
  }

  /**
   * 获取筛选参数
   */
  getFilterFormData(dataId: string, componentId: string): KeyValueStore | null {
    const gridFilters = this.dataStore.gridList.filter((gridItem) => {
      return gridItem.chartType === 'filter' && gridItem.dataId === dataId
    })
    if (gridFilters.length > 0) {
      const result: KeyValueStore = {}
      gridFilters.forEach((item) => {
        item.fieldConfig.forEach((fieldInfo: ControlConfigurationInterface) => {
          if (fieldInfo.associationChart.includes(componentId)) {
            result[fieldInfo.column.value] = {
              compType: fieldInfo.controlRender,
              columnType: fieldInfo.columnType,
              value: fieldInfo.value,
            }
          }
        })
      })
      return result
    }
    return null
  }
  getFilterItem(
    fieldName: string,
    value: any,
    controlType: string,
    columnType?: string
  ): KeyValueStore | null {
    let result: any = null
    switch (controlType) {
      case componentTypeEnum.radio:
      case componentTypeEnum.select:
        if (isString(value) && value !== '') {
          result = {
            col: fieldName,
            filterType: '=',
            values: [],
            checkedList: [value],
          }
        }
        break

      case componentTypeEnum.checkbox:
        if (value && isArray(value) && value.length > 0) {
          result = {
            col: fieldName,
            type: columnType,
            filterType: 'in',
            values: [],
            checkedList: value,
          }
        }
        break

      case componentTypeEnum.rangeDay:
        if (
          value &&
          isArray(value) &&
          value.length === 2 &&
          value[0] &&
          value[1]
        ) {
          result = {
            col: fieldName,
            filterType: '[a,b]',
            type: 'timestamp', // timestamp | datetime
            values: [value[0], value[1]],
            format: 'yyyy-MM-dd',
            checkedList: [value[0], value[1]],
          }
        }
        break
      case componentTypeEnum.rangeMonth:
        if (
          value &&
          isArray(value) &&
          value.length === 2 &&
          value[0] &&
          value[1]
        ) {
          result = {
            col: fieldName,
            filterType: '[a,b]',
            type: 'timestamp', // timestamp | datetime
            values: [value[0], value[1]],
            format: 'yyyy-MM',
            checkedList: [value[0], value[1]],
          }
        }

        break
      case componentTypeEnum.rangeYear:
        if (
          value &&
          isArray(value) &&
          value.length === 2 &&
          value[0] &&
          value[1]
        ) {
          result = {
            col: fieldName,
            filterType: '[a,b]',
            type: 'timestamp', // timestamp | datetime
            values: [value[0], value[1]],
            format: 'yyyy',
            checkedList: [value[0], value[1]],
          }
        }
        break
      default:
        break
    }

    return result
  }

  getTextData(gridItem: GridItemDataInterface) {
    if (gridItem && gridItem.chartOptions && gridItem.chartType === 'text') {
      const dataSend: KeyValueStore = {
        data: {
          id: gridItem.widgetId,
          type: gridItem.chartType, //  'text'
        },
      }
      this.$set(gridItem, 'isLoading', true)
      getWidgetData(dataSend)
        .then((response: any) => {
          if (response.data.code === 100) {
            this.$set(
              gridItem,
              'chartOptions',
              response.data.result.extraData.chartOptions
            )
          }
          this.$set(gridItem, 'isLoading', false)
        })
        .catch(() => {
          this.$set(gridItem, 'isLoading', false)
        })
    } else {
      console.error('call error: getTableData must be used by table')
    }
  }

  getTableData(gridItem: GridItemDataInterface, withFilterSend?: boolean) {
    if (gridItem && gridItem.chartOptions && gridItem.chartType === 'table') {
      /** 表格组件有点特殊，自带分页组件，所以在关联状态时要做二次确认 */
      const withFilter =
        this.currentFilterFormAsCharts.includes(gridItem.widgetId) ||
        withFilterSend
      const filterFormJson =
        withFilter && gridItem.filterFormJson ? gridItem.filterFormJson : []
      const dataSend: KeyValueStore = {
        data: {
          id: gridItem.chartOptions.dataId,
          type: gridItem.chartOptions.dataType,
          tableJson: {
            curPage: withFilter ? 1 : gridItem.chartOptions.curPage,
            name: '_record_id_',
            pageSize: gridItem.chartOptions.pageSize,
            sortCol: gridItem.chartOptions.sortCol, //  sortCol; interact info in -> filters
            filters: withFilter
              ? mergeFilterFormFilters(
                  gridItem.chartOptions.filter || [],
                  filterFormJson
                )
              : gridItem.chartOptions.filter,
          },
        },
      }
      this.$set(gridItem, 'isLoading', true)

      getWidgetData(dataSend)
        .then((response: any) => {
          if (response.data.code === 1106) {
            this.errorFilterWidgets.push(gridItem.i)
            this.errorMsgMap[gridItem.i] = response.data.message
          }
          if ([100, 1106].includes(response.data.code)) {
            const gridItemClone = cloneDeep(gridItem)
            const { chartOptions } = gridItemClone
            const index = this.getGridIndex(gridItem.i as string, 'i')
            const gridItemSet = {
              ...gridItemClone,
              isLoading: false,
              chartOptions: {
                ...chartOptions,
                curPage: response.data.result?.page?.curPage || 0,
                totalElements: response.data.result?.page?.totalElements || 0,
                pageSize: response.data.result?.page?.pageSize || 50,
                value: response.data.result
                  ? parseGetDataResult(response.data.result)
                  : [],
              },
            }
            VisualizationStore.updateWidget({
              gridItem: gridItemSet,
              index,
              onlyLocal: true,
            })
          } else {
            this.$set(gridItem, 'isLoading', false)
          }
          if (withFilter) {
            setTimeout(() => {
              this.releaseLoadingTag(gridItem.widgetId)
            }, 100)
          }
        })
        .catch(() => {
          this.$set(gridItem, 'isLoading', false)
          if (withFilter) {
            setTimeout(() => {
              this.releaseLoadingTag(gridItem.widgetId)
            }, 100)
          }
        })
    } else {
      this.releaseLoadingTag(gridItem.widgetId)
      console.error('call error: getTableData must be used by table')
    }
  }

  /**
   * 根据 gridItem 获取graph widget 数据
   */
  private async getGraphData(
    gridItem: GridItemDataInterface,
    withFilter?: boolean
  ) {
    this.$set(gridItem, 'isLoading', true)
    const callback = withFilter
      ? (data: any) => filterGraphData(gridItem.filterFormJson || [], data)
      : undefined
    const value = await getWidgetGraphData(gridItem, this.projectId, callback)
    /** 将数据缓存到graphService中，以便筛选组件使用 */
    // visGraphService.save({id: gridItem.chartOptions?.dataId || '', data: value})
    const { nodePosition = [] } = gridItem.chartOptions
    value.nodes.forEach((node: any) => {
      const targetPosition = (nodePosition as Array<{
        id: string
        x: number
        y: number
      }>).filter((p) => p.id === node.id)[0]
      if (targetPosition) {
        node.x = targetPosition.x
        node.y = targetPosition.y
      }
    })
    this.$set(gridItem.chartOptions, 'value', value)
    // this.$set(gridItem, 'chartType', chartTypeOld)
    this.$set(gridItem, 'showChart', true)
    this.$set(gridItem, 'isLoading', false)
    setTimeout(() => {
      this.releaseLoadingTag(gridItem.widgetId)
    }, 100)
  }

  /** 用于存储code为1106的response的widget */
  public errorFilterWidgets = [] as Array<any>
  /** 错误消息 */
  public errorMsgMap = {} as any

  /**
   * 获取widget数据
   */
  getWidgetData(
    dataSetting: KeyValueStore,
    gridItem: GridItemDataInterface,
    index: number,
    isAutoSave?: boolean,
    withFilter?: boolean,
    keepCurrent?: boolean
  ) {
    const filters: KeyValueStore | null = this.getFilterFormData(
      dataSetting.dataId,
      gridItem.i as string
    )
    const filterSend = [] as Array<any>
    if (filters) {
      Object.keys(filters).forEach((fieldName: string) => {
        const controlRender = filters[fieldName].compType
        const value = filters[fieldName].value as any
        if (
          (value &&
            !['rangeDay', 'rangeYear', 'rengeMonth'].includes(controlRender) &&
            value[0] !== '') ||
          (value &&
            ['rangeDay', 'rangeYear', 'rengeMonth'].includes(controlRender) &&
            value[0] &&
            value[1])
        ) {
          const filterItem = this.getFilterItem(
            fieldName,
            value,
            controlRender,
            filters[fieldName].columnType
          )
          if (filterItem) {
            filterSend.push(filterItem)
          }
        }
      })
    }
    const filterFormJson = gridItem.filterFormJson || []

    if (gridItem.chartType === 'table') {
      this.getTableData(gridItem, withFilter)
      return
    }
    if (gridItem.chartType === 'text') {
      this.getTextData(gridItem)
      return
    }
    if (
      GRAPH_ARRAY.includes(
        Array.isArray(gridItem.chartType)
          ? gridItem.chartType[1]
          : gridItem.chartType
      )
    ) {
      this.getGraphData(gridItem, withFilter)
      return
    }

    const query = {
      data: gridItem.dataQuery?.data,
    }
    ;(<any>query.data.widgetJson.config.filters) = filterSend
    /*
    gridItem.chartOptions = {
      ...gridItem.chartOptions,
      ...{
        labelIsShow: false,
      },
    }
     */
    if (enableConvertToCoor(gridItem.chartOptions)) {
      // 需要映射地理数据（省份、城市、ip等映射成经纬度）
      query.data.convert = gridItem.chartOptions.geoTransform.fields
    }

    /** 筛选数据 */
    if (withFilter && query.data?.widgetJson?.config) {
      query.data.widgetJson.config.filters = mergeFilterFormFilters(
        query.data.widgetJson.config.filters || [],
        filterFormJson
      )
    }

    this.$set(gridItem, 'isLoading', true)
    this.errorFilterWidgets = this.errorFilterWidgets.filter(
      (item) => item !== gridItem.i
    )
    getWidgetData(query)
      .then((response) => {
        if (response.data.code === 1106) {
          this.errorFilterWidgets.push(gridItem.i)
          this.errorMsgMap[gridItem.i] = response.data.message
        }
        /** 在筛选器模式下有可能筛选不出数据，状态为1106 */
        if ([100, 1106].includes(response.data.code)) {
          setTimeout(() => {
            this.$set(VisualizationStore.gridList[index], 'isLoading', false)
          }, 100)

          const chartType = Array.isArray(gridItem.chartType)
            ? gridItem.chartType[1]
            : gridItem.chartType
          if (isAutoSave) {
            const parameters = buildSaveParameters(gridItem)
            // parameters.data.tid = gridItem.dataId
            // parameters.data.type = 'task'
            if (!parameters.projectId) {
              parameters.projectId = this.projectId
            }
            if (this.dashBoardId) {
              parameters.data.dashboardId = this.dashBoardId
            }
            saveWidget(parameters).then((responsesave) => {
              if (responsesave.data.code === 100) {
                this.$set(gridItem, 'widgetId', responsesave.data.result)
                // this.$set(gridItem.formData, 'dataId',  gridItem.dataId)
                // 处理数据
                let dataResult =
                  parseGetDataResult(
                    response.data?.result || {
                      columns: [],
                      data: [],
                      extraData: {},
                    },
                    chartType,
                    gridItem
                  ) || []
                if (query.data.widgetJson !== undefined) {
                  dataResult = restructGetDataResult(
                    dataResult,
                    query.data.widgetJson
                  )
                }
                VisualizationStore.gridItemSyncBindData({
                  data: {
                    query,
                    data: dataResult,
                    columns: gridItem.columns,
                    formData: gridItem.chartOptions,
                  },
                  gridItem,
                  index,
                  withFilter,
                  noChangeCurrent: !!keepCurrent,
                })
                console.error('getWidgeData save done')
                this.onSave()
              }
            })
          } else {
            let dataResult = parseGetDataResult(
              response.data?.result || { columns: [], data: [], extraData: {} },
              chartType,
              gridItem
            )
            if (query.data.widgetJson !== undefined) {
              dataResult = restructGetDataResult(
                dataResult,
                query.data.widgetJson
              )
            }
            VisualizationStore.gridItemSyncBindData({
              data: {
                query,
                data: dataResult,
                columns: gridItem.columns,
                formData: gridItem.chartOptions,
              },
              gridItem,
              index,
              withFilter,
              noChangeCurrent: !!keepCurrent,
            })
          }
        } else {
          this.$set(gridItem, 'isLoading', false)
        }
        if (withFilter) {
          setTimeout(() => {
            this.releaseLoadingTag(gridItem.widgetId)
          }, 100)
        }
      })
      .catch((error) => {
        console.log('error', error)
        this.$set(gridItem, 'isLoading', false)
        if (withFilter) {
          setTimeout(() => {
            this.releaseLoadingTag(gridItem.widgetId)
          }, 100)
        }
      })
  }

  /** filter是否可用 */
  isFilterEnabled(gridItem: GridItemDataInterface) {
    return checkFilterFormIsEnabled(gridItem)
  }

  /** 检测filterForm是否正在执行 */
  get isFilterFormRunning() {
    return VisualizationStore.isSettingPanelChangeLock
  }
  /** 释放加载标识 */
  releaseLoadingTag(widgetId: string | number) {
    this.loadingMaps[`widget_${this.filterFormExecuteId}`] =
      this.loadingMaps[`widget_${this.filterFormExecuteId}`] || []
    this.$set(
      this.loadingMaps,
      `widget_${this.filterFormExecuteId}`,
      this.loadingMaps[`widget_${this.filterFormExecuteId}`].filter(
        (id: any) => id !== widgetId
      )
    )

    setTimeout(() => {
      if (this.loadingMaps[`widget_${this.filterFormExecuteId}`].length === 0) {
        VisualizationStore.setIsSettingPanelChangeLock(false)
      }
    }, 1000)
  }

  gridItemClick(item: GridItemDataInterface, index: number = -1) {
    const isGraph =
      item.chartType &&
      isArray(item.chartType) &&
      item.chartType[1] === 'graph_net'
    if (isGraph) {
      VisualizationStore.setCurrentGridItem(null)
      VisualizationStore.setActiveSettingItem('background')
      VisualizationStore.setSettingMode('property') // 将设置模式归位
      this.$message.error('graph 组件暂不支持配置')
      return
    }

    VisualizationStore.setCurrentGridItem(item)
    if (item.chartType === 'text') {
      VisualizationStore.setActiveSettingItem('text')
      //  set item non-draggable when edit mode; click item fetch formulaList;
      if (index === -1) {
        // VisualizationStore.fetchFormulaList({
        //   projectId: Number(this.$route.params.id),
        // })
      } else {
        this.$set(this.gridList[index], 'draggable', false)
        //  set toolbar position align with text comp
        const toolbar: any = document.querySelector(
          `.box-item-${item.i} .tox-tinymce.tox-tinymce-inline`
        )
        const target: any = document.querySelector(`#grid-item-${item.i}`)
        toolbar.style.transform = target.style.transform
        toolbar.style.width = target.style.width
        toolbar.style.height = '40px'
        toolbar.style.opacity = 1
      }

      return
    }

    VisualizationStore.setActiveSettingItem('chart')
  }

  getGridStyle(item: GridItemDataInterface) {
    const dataStore = VisualizationStore
    const { currentGridItem, activeSettingItem } = dataStore
    if (
      item.chartOptions.pipelineId &&
      item.chartOptions.pipelineId ===
        currentGridItem?.chartOptions.pipelineId &&
      activeSettingItem === 'chart'
    ) {
      return `border: 1px solid ${
        currentGridItem?.i === item.i ? '#6973FF' : '#c5c9ff'
      };box-shadow: 0px 0px 4px 0px rgba(88,98,230,0.2);`
    }
    // 为确保和组件联动样式一致，这里将当前选中组件边框颜色改为 rgb(105, 115, 255) 原先为 rgba(87, 96, 230,0.45)
    return dataStore.currentGridItem &&
      dataStore.currentGridItem.i === item.i &&
      dataStore.activeSettingItem !== 'background'
      ? 'border: 1px solid rgba(87, 96, 230,0.45); box-shadow: 0 0 10px 0 rgba(87, 96, 230,0.25)'
      : ''
  }

  isEditShow(item: GridItemDataInterface) {
    if (item.chartType === 'filter') {
      return true
    }
    return (
      item.formData &&
      item.formData.dataId > 0 &&
      item.chartOptions &&
      item.chartOptions.value &&
      item.chartOptions.value.length > 0
    )
  }

  private lastBindIds: KeyValueStore = {}

  // editChart(
  //   currentItem: GridItemDataInterface,
  //   index: number,
  //   associationChart?: GridItemDataInterface
  // ) {
  //   if (currentItem.chartType === 'filter') {
  //     const currrentGridItem = currentItem as gridItemFilterInterface
  //     const tabsInfo: Array<KeyValueStore> = []
  //     currrentGridItem.fieldConfig.forEach((info) => {
  //       const setting = getFormSettingByComponent(info.controlRender)
  //       Object.keys(setting.formData).forEach((key) => {
  //         if ((info as any)[key]) {
  //           setting.formData[key] = (info as any)[key]
  //         }
  //       })
  //       // 添加关联图表
  //       if (
  //         associationChart &&
  //         setting.formData.associationChart.length === 0 &&
  //         !setting.formData.associationChart.includes(associationChart.i)
  //       ) {
  //         setting.formData.associationChart.push(associationChart.i)
  //       }
  //       tabsInfo.push({
  //         ...info.column,
  //         column: info.column, // 用于反向转换
  //         formConfig: setting,
  //         openAdvancedSetting: false,
  //         currentPage: 1,
  //         pageSize: 10,
  //         optionsAll: [],
  //         options: cloneDeep(info.options),
  //       })
  //     })
  //     this.$set(this.dialogControllerSettingConfig, 'gridItem', {
  //       formData: {
  //         dataId: currrentGridItem.dataId,
  //         tableName: currrentGridItem.tableName,
  //         tableNameReal: currrentGridItem.tableName,
  //       },
  //     })
  //     this.$set(this.dialogControllerSettingConfig, 'visible', true)
  //     this.$set(
  //       this.dialogControllerSettingConfig,
  //       'layout',
  //       currrentGridItem.layout
  //     )
  //     this.$set(
  //       this.dialogControllerSettingConfig,
  //       'from',
  //       associationChart || currentItem
  //     )
  //     this.lastBindIds = this.getBindChartIds(currrentGridItem)
  //     setTimeout(() => {
  //       const dialog = this.$refs['dialog-contrller-setting'] as any
  //       if (dialog) {
  //         dialog.syncTableInfo()
  //         setTimeout(() => {
  //           dialog.setEditGridItem(currrentGridItem)
  //           dialog.setTabsInfo(tabsInfo, 0)
  //         }, 10)
  //       }
  //     }, 10)
  //   } else {
  //     const controlGridItem = this.dataStore.gridList.find(
  //       (gridItem) =>
  //         gridItem.chartType === 'filter' &&
  //         gridItem.dataId &&
  //         currentItem.formData &&
  //         currentItem.formData.dataId &&
  //         gridItem.dataId === currentItem.formData.dataId
  //     )
  //     if (controlGridItem) {
  //       this.editChart(controlGridItem, 0, currentItem)
  //     } else {
  //       this.$set(this.dialogControllerSettingConfig, 'gridItem', currentItem)
  //       this.$set(this.dialogControllerSettingConfig, 'from', currentItem)
  //       this.$set(
  //         this.dialogControllerSettingConfig,
  //         'layout',
  //         controlLayoutEnum.vertical
  //       )
  //       this.$set(this.dialogControllerSettingConfig, 'visible', true)
  //       setTimeout(() => {
  //         const dialog = this.$refs['dialog-contrller-setting'] as any
  //         dialog.syncTableInfo()
  //         dialog.setEditGridItem(null)
  //       }, 10)
  //     }
  //   }
  // }

  onAschartchange(data: any) {
    const newValueSet = new Set(data?.newValue || [])
    /** 解决table在取消勾选后仍然无法重置的情况 */
    VisualizationStore.setCurrentFilterFormAsCharts(data?.newValue || [])
    const diff = (data.old || []).filter((item: any) => !newValueSet.has(item))
    diff.forEach((id: string | number) => {
      let index = -1
      const find = this.dataStore.gridList.find(
        (grid: GridItemDataInterface, i: number) => {
          index = i
          return grid.widgetId === id
        }
      )
      if (find) {
        this.getWidgetData(find.chartOptions, find, index, false, false, true)
      }
    })
  }

  /*  检查并清除filter-form的关联chart */
  clearFilterFormAssociationChart(widgetId: string | number) {
    VisualizationStore.gridList.forEach(
      (gridItem: GridItemDataInterface, index: number) => {
        if (
          checkIsFilterForm(gridItem) &&
          ((gridItem.chartOptions?.associationChart || []) as Array<
            string | number
          >).includes(widgetId)
        ) {
          const { chartOptions } = gridItem
          this.$set(
            chartOptions,
            'associationChart',
            chartOptions.associationChart.filter(
              (id: string | number) => id !== widgetId
            )
          )
          VisualizationStore.updateWidget({
            gridItem: {
              ...gridItem,
              chartOptions,
            },
            index,
          })
        }
      }
    )
  }

  deleteItem(item: GridItemDataInterface, index: number) {
    const chartId = item.i as string
    const dataId = item.chartOptions.dataId as string
    const needDelControls: Array<string> = []
    // 同步清空关联控件
    VisualizationStore.gridList.forEach((gridItem) => {
      if (gridItem.chartType === 'filter' && gridItem.dataId === dataId) {
        const fieldConfigNew = [] as Array<ControlConfigurationInterface>
        ;(gridItem.fieldConfig as Array<ControlConfigurationInterface>).forEach(
          (fieldInfo) => {
            if (fieldInfo.associationChart.includes(chartId)) {
              if (fieldInfo.associationChart.length > 1) {
                fieldInfo.associationChart = fieldInfo.associationChart.filter(
                  (chartIdField) => chartIdField !== chartId
                )
                fieldConfigNew.push(fieldInfo)
              }
            } else {
              fieldConfigNew.push(fieldInfo)
            }
          }
        )
        if (fieldConfigNew.length > 0) {
          this.$set(gridItem, 'fieldConfig', fieldConfigNew)
        } else {
          needDelControls.push(gridItem.i as string)
        }
      }
    })

    if (item.widgetId && !checkIsFilterForm(item)) {
      this.clearFilterFormAssociationChart(item.widgetId)
    }
    VisualizationStore.setCurrentGridItem(null)
    VisualizationStore.setActiveSettingItem('background')
    VisualizationStore.removeGridItem(index, 1)
    VisualizationStore.setRecommendBaseSpec(null)
    if (needDelControls.length > 0) {
      const gridListNew = VisualizationStore.gridList.filter(
        (gridItem) => !needDelControls.includes(gridItem.i as string)
      )
      VisualizationStore.setGridList(gridListNew)
      setTimeout(() => {
        this.flexLayout()
      }, 10)
    } else {
      this.onSave()
    }
  }

  getBindChartIds(item: gridItemFilterInterface) {
    const chartids: KeyValueStore = {}
    if (item.chartType === 'filter') {
      item.fieldConfig.forEach((fieldItem: KeyValueStore) => {
        if (
          fieldItem.associationChart &&
          fieldItem.associationChart.length > 0
        ) {
          ;(fieldItem.associationChart as Array<string>).forEach((chartId) => {
            chartids[chartId] = chartId
          })
        }
      })
    }
    return chartids
  }

  deleteChart(item: GridItemDataInterface, index: number) {
    // const isGraph =
    //   item.chartType &&
    //   isArray(item.chartType) &&
    //   item.chartType[1] === GRAPH_NAME
    // if (isGraph) {
    //   // 删除graph, 这里暂时不用考虑控件的问题
    //   VisualizationStore.removeGridItem(index, 1)
    //   VisualizationStore.setCurrentGridItem(null)
    //   VisualizationStore.setActiveSettingItem('background')
    //   this.$nextTick(() => {
    //     this.onSave(true)
    //   })
    // } else
    if (!item.widgetId) {
      if (item.chartType === 'table') {
        this.deleteItem(item, index)
      } else {
        let chartids: KeyValueStore = {}
        if (item.chartType === 'filter') {
          chartids = this.getBindChartIds(item as gridItemFilterInterface)
        }
        VisualizationStore.removeGridItem(index, 1)
        VisualizationStore.setCurrentGridItem(null)
        VisualizationStore.setActiveSettingItem('background')
        this.$nextTick(() => {
          this.onSave(true, () => {
            setTimeout(() => {
              VisualizationStore.gridList.forEach((gridItem, gridItemindex) => {
                if (gridItem.chartType !== 'filter' && chartids[gridItem.i]) {
                  if (
                    (gridItem.chartType === 'table' || gridItem.widgetId) &&
                    gridItem.formData &&
                    gridItem.formData.dataId
                  ) {
                    this.getWidgetData(
                      gridItem.formData as KeyValueStore,
                      gridItem,
                      gridItemindex
                    )
                  }
                }
              })
            }, 500)
          })
        })
      }
    } else {
      deleteWidget({
        data: {
          projectId: this.projectId,
          id: item.widgetId,
        },
      }).then(() => {
        this.deleteItem(item, index)
      })
    }
  }

  onChartInstanceInit(chartInstance: any, item: GridItemDataInterface) {
    VisualizationStore.setChartInstanceMaps({
      instance: chartInstance,
      key: item.i,
    })
  }

  onLayoutContentClick(event: any) {
    const $element = event.target as HTMLElement
    if ($element && $element.className && $element.className.includes) {
      const classNameArray = $element.className
      if (
        classNameArray.includes('grid-layout-design') ||
        classNameArray.includes('vue-grid-layout')
      ) {
        VisualizationStore.setActiveSettingItem('background')
      }
    }
    event.stopPropagation()
  }

  getOffset(target: any): { x: number; y: number } {
    let ele = target
    let o = ele
    let x = 0
    let y = 0
    while (o.offsetParent) {
      x += o.offsetLeft
      y += o.offsetTop
      o = o.offsetParent
    }
    // 处理当元素处于滚动之后的情况
    let left = 0
    let top = 0
    while (ele.parentNode) {
      left += ele.scrollLeft
      top += ele.scrollTop
      ele = ele.parentNode
    }
    return {
      x: left - x,
      y: top - y,
    }
  }
  getChartImage(chartType: Array<string> | string) {
    return getChartIconImage(chartType)
  }
  getDefaultName(chartType?: string) {
    let chartTypeUsed: string = ''

    if (chartType) {
      if (isArray(chartType)) {
        ;[, chartTypeUsed] = chartType
      } else if (/\|/.test(chartType)) {
        ;[, chartTypeUsed] = chartType.split('|')
      } else {
        chartTypeUsed = chartType
      }
    }

    let result = ''
    const nameAll = VisualizationStore.gridList.reduce(
      (rec: Array<string>, item: any) => {
        if (item.formData) {
          rec.push(item.formData.title || '')
        }
        if (item.chartOptions && item.chartOptions.title) {
          rec.push(item.chartOptions.title)
        }
        return rec
      },
      []
    )
    let prefix =
      chartTypeUsed && chartTypes[chartTypeUsed]
        ? chartTypes[chartTypeUsed].title
        : '组件'
    if (
      chartTypeUsed &&
      Object.prototype.hasOwnProperty.call(chartTypesSub, chartTypeUsed)
    ) {
      prefix = (chartTypesSub as any)[chartTypeUsed]
        ? (chartTypesSub as any)[chartTypeUsed].title
        : '组件'
    }
    prefix = prefix || '组件'
    if (chartTypeUsed === 'groupBarChart') {
      prefix = '分组柱状图'
    } else if (chartTypeUsed === 'stackBarChart') {
      prefix = '堆叠柱状图'
    } else if (chartTypeUsed === 'donutChart') {
      prefix = '圆环图'
    }
    for (let i = 1; i < 5000; i += 1) {
      const name = `${prefix}${i}`
      if (!nameAll.includes(name)) {
        result = name
        break
      }
    }
    return result
  }

  onDragOver(event: any) {
    event.preventDefault()
  }

  private async onDropText(event: any) {
    const offset = this.dataStore.draggingOffset
    const x =
      ((event.layerX - offset.x - 10) / VisualizationStore.scaleValue) * 100
    const y = ((event.layerY - offset.y) / VisualizationStore.scaleValue) * 100
    const title = this.getDefaultName('text')
    const configuration: GridItemDataInterface = {
      chartOptions: {
        chartType: 'text',
        title,
        value: '',
        showBorder: true,
        showTitle: true,
      },
      chartType: 'text',
      x: Math.ceil(x / this.unitX) < 0 ? 0 : Math.ceil(x / this.unitX),
      y: Math.floor(y / this.unitY) < 0 ? 0 : Math.floor(y / this.unitY),
      w: Math.ceil(598 / this.unitX),
      h: Math.ceil(400 / this.unitY),
      i: `text${Math.random().toString(36).slice(-8)}`,
      minW: Math.ceil(50 / this.unitX),
      minH: Math.ceil(50 / this.unitY),
      resizable: true,
      moved: true,
      widgetType: 'system',
    }
    const parameters = buildSaveParameters(configuration)
    if (this.dashBoardId) {
      parameters.data.dashboardId = this.dashBoardId
    }
    const response: any = await saveWidget(parameters) // 保存configuration widget
    configuration.widgetId = response.data.result

    VisualizationStore.addGridItem(configuration)
    setTimeout(() => {
      VisualizationStore.setActiveSettingItem('text')
      // VisualizationStore.fetchFormulaList({
      //   projectId: Number(this.$route.params.id),
      // })

      VisualizationStore.setCurrentGridItem(
        this.gridList[this.gridList.length - 1]
      )
      this.onSave()
    }, 100)
    //  bug fix for enter scroll issue
    this.$nextTick(() => {
      const element: any = document.querySelector(
        `#grid-item-${configuration.i} .text-editor`
      )
      element.parentNode.style.height = 'calc(100% - 38px)'
    })
  }

  private updateActiveGridItem() {
    setTimeout(() => {
      VisualizationStore.setActiveSettingItem('chart')

      VisualizationStore.setCurrentGridItem(
        this.gridList[this.gridList.length - 1]
      )
    }, 100)
  }

  private async onDropTable(event: any, chartInfo?: string) {
    // chartInfo: widgetId & dataId $ dataType $chartType
    // chartInfo: dataId$dataType$chartType
    const offset = this.dataStore.draggingOffset
    const x =
      ((event.layerX - offset.x - 10) / VisualizationStore.scaleValue) * 100
    const y = ((event.layerY - offset.y) / VisualizationStore.scaleValue) * 100
    const title = this.getDefaultName('table')
    const chartInfoArray = (chartInfo || '').split('$')
    let find: any = null
    if (chartInfo) {
      find = VisualizationStore.pipelineWidgetList.find(
        (pipelineListItem: any) => {
          return (
            !pipelineListItem.id &&
            pipelineListItem.tid === Number(chartInfoArray[1]) &&
            pipelineListItem.type === 'task'
          )
        }
      )
    }
    const tableNew: GridItemDataInterface = {
      // formData: {
      //   title,
      //   dataId: '',
      //   column: [],
      //   curPage: 1,
      //   total: 0,
      //   name: '_record_id_',
      //   pageSize: 50,
      //   filter: [],
      //   table: '',
      // },
      chartOptions: {
        title,
        value: [],
        dataId: chartInfo ? chartInfoArray[1] : '',
        dataType: chartInfo ? chartInfoArray[2] : 'dataset',
        // column: [],
        curPage: 1,
        total: 0,
        name: '_record_id_',
        pageSize: 50,
        filters: [],
        sortCol: [],
        table: '',
      },
      chartType: 'table',
      x: Math.ceil(x / this.unitX) < 0 ? 0 : Math.ceil(x / this.unitX),
      y: Math.floor(y / this.unitY) < 0 ? 0 : Math.floor(y / this.unitY),
      w: Math.ceil(498 / this.unitX),
      h: Math.ceil(300 / this.unitY),
      i: `table${Math.random().toString(36).slice(-8)}`,
      minW: Math.ceil(400 / this.unitX),
      minH: Math.ceil(300 / this.unitY),
      resizable: true,
      moved: true,
      widgetType: chartInfo ? 'pipeline' : 'system',
    }
    //  for save widget get widgetId
    const parameters = buildSaveParameters(tableNew)
    if (this.dashBoardId) {
      parameters.data.dashboardId = this.dashBoardId
    }

    const response: any = await saveWidget(parameters) // 为table 创造 widgetId
    tableNew.widgetId = response.data.result

    if (find) {
      tableNew.chartOptions.pipelineId = find.pipelineId
    }
    VisualizationStore.addGridItem(tableNew)
    this.updateActiveGridItem()
  }

  /**
   * 拖动放置算子控件组件
   */
  async onDropConfiguration(event: any, chartInfo?: string) {
    const offset = this.dataStore.draggingOffset
    const x =
      ((event.layerX - offset.x - 10) / VisualizationStore.scaleValue) * 100
    const y = ((event.layerY - offset.y) / VisualizationStore.scaleValue) * 100
    const chartInfoArray = (chartInfo || '').split('$')
    const find = VisualizationStore.pipelineWidgetList.find(
      (pipelineListItem: any) => {
        return pipelineListItem.tid === Number(chartInfoArray[1])
      }
    )
    if (find) {
      const configuration: GridItemDataInterface = {
        chartOptions: {
          chartType: 'configuration',
          params: find.data.params.map((p: any) => ({
            ...p,
            status: 'normal',
          })),
          info: find.data.info,
          pipelineId: find.pipelineId,
          taskId: find.tid,
          dataId: find.tid,
          title: find.name,
          titleIsShow: true,
          value: find.data.params.reduce((accu: any, current: any) => {
            accu[current.name] =
              current.value !== undefined ? current.value : current.default
            return accu
          }, {}),
        },
        chartType: 'configuration',
        x: Math.ceil(x / this.unitX) < 0 ? 0 : Math.ceil(x / this.unitX),
        y: Math.floor(y / this.unitY) < 0 ? 0 : Math.floor(y / this.unitY),
        w: Math.ceil(498 / this.unitX),
        h: Math.ceil(300 / this.unitY),
        i: `configuration${Math.random().toString(36).slice(-8)}`,
        minW: Math.ceil(200 / this.unitX),
        minH: Math.ceil(50 / this.unitY),
        resizable: true,
        moved: true,
        widgetType: 'pipeline',
      }
      const parameters = buildSaveParameters(configuration)
      if (this.dashBoardId) {
        parameters.data.dashboardId = this.dashBoardId
      }
      const response: any = await saveWidget(parameters) // 保存configuration widget
      configuration.widgetId = response.data.result

      VisualizationStore.addGridItem(configuration)
      this.updateActiveGridItem()
    }
  }

  /** 重新计算尺寸与坐标裁切画布空白部分 */
  clipGraphData(graphData: any) {
    return clipGraphData(
      graphData,
      this.dataStore.layoutData.width,
      this.dataStore.layoutData.height
    )
  }

  // onDropGraph(event: any, chartInfo: string) {
  //   const parseArray = chartInfo.split('$')
  //   const info: any = {
  //     tid: parseArray[2],
  //     id: parseArray[0],
  //     chartType: parseArray[1].split('|'),
  //   }
  //   let find = VisualizationStore.pipelineWidgetList.find(
  //     (pipelineListItem: any) => {
  //       return pipelineListItem.id === Number(info.id)
  //     }
  //   )
  //   let myComp: KeyValueStore | null = null
  //   if (find) {
  //     find = this.clipGraphData(find)
  //     const defaultSize: [number, number] = find.data?.size
  //       ? find.data?.size
  //       : [400, 400]
  //     info.title = find.name
  //     const uuid = `${GRAPH_NAME}${Math.random().toString(36).slice(-8)}`
  //     const offset = this.dataStore.draggingOffset
  //     const x =
  //       ((event.layerX - offset.x - 10) / VisualizationStore.scaleValue) * 100
  //     const y =
  //       ((event.layerY - offset.y) / VisualizationStore.scaleValue) * 100
  //     myComp = {
  //       chartType: [GRAPH_NAME, GRAPH_NAME],
  //       dataId: info.tid,
  //       formData: info,
  //       h: Math.ceil(defaultSize[1] / this.unitY),
  //       i: uuid,
  //       isLoading: false,
  //       moved: false,
  //       w: Math.ceil(defaultSize[0] / this.unitX),
  //       widgetId: info.id,
  //       widgetJson: {},
  //       x: Math.ceil(x / this.unitX) < 0 ? 0 : Math.ceil(x / this.unitX),
  //       y: Math.floor(y / this.unitY) < 0 ? 0 : Math.floor(y / this.unitY),
  //       minW: Math.ceil(400 / this.unitX),
  //       minH: Math.ceil(400 / this.unitY),
  //       chartOptions: {
  //         size: defaultSize,
  //         ...getDefaultGraphConfig(find.data, defaultSize, uuid, 'name'),
  //         title: find.name,
  //       },
  //     }
  //     myComp.chartOptions.view.size = myComp.chartOptions.size
  //     VisualizationStore.addGridItem(myComp as any)
  //     setTimeout(() => {
  //       VisualizationStore.setActiveSettingItem('background')
  //       VisualizationStore.setCurrentGridItem(
  //         this.gridList[this.gridList.length - 1]
  //       )
  //     }, 100)
  //   }
  // }

  /** 拖入geomap组件 */
  onDropGeoMap(event: any, chartInfo: string) {
    let myComp: KeyValueStore | null = null
    const dataId = chartInfo.split('$')[0] as string // 其实是taskId
    const find = VisualizationStore.pipelineWidgetList.find(
      (pipelineListItem: any) => {
        return pipelineListItem.id === Number(dataId)
      }
    )
    if (find) {
      myComp = cloneDeep(find.data)
      if (myComp) {
        const offset = this.dataStore.draggingOffset
        const x =
          ((event.layerX - offset.x - 10) / VisualizationStore.scaleValue) * 100
        const y = event.layerY - offset.y
        myComp.chartOptions.size = [400, 300]
        const valueKey = myComp.chartOptions.valueKey as any
        if (valueKey === '' || (isArray(valueKey) && valueKey.length === 0)) {
          myComp.chartOptions.valueKey = [
            { value: '', sort: 'asc', func: 'sum' },
          ]
        }
        // myComp.chartOptions.isVisualization = true
        myComp.x = Math.ceil(x / this.unitX) < 0 ? 0 : Math.ceil(x / this.unitX)
        myComp.y =
          Math.floor(y / this.unitY) < 0 ? 0 : Math.floor(y / this.unitY)
        myComp.w = Math.ceil(400 / this.unitX)
        myComp.h = Math.ceil(300 / this.unitY)
        myComp.chartOptions = {
          ...GeoMapDefaultConfig,
          ...myComp.chartOptions,
        }
        myComp.chartOptions.pipelineName = find.pipelineName
        myComp.chartOptions.taskName = find.taskName
        myComp.chartOptions.tableNameReal = find.data.chartOptions.tableName
        myComp.i = `${
          chartInfo.split('|')[1] ? chartInfo.split('|')[1] : 'id'
        }${Math.random().toString(36).slice(-8)}`

        if (!myComp.chartOptions.title) {
          myComp.chartOptions.title = this.getDefaultName(GEOMAP_NAME)
        }
        myComp.dataQuery = {
          data: {
            id: myComp.chartOptions.dataId,
            type: myComp.chartOptions.dataType,
            widgetJson: {
              ...myComp.widgetJson,
            },
          },
        }

        // const result = TransDropGeoMapGridItem(
        //   myComp as gridItemFilterInterface
        // )
        // VisualizationStore.addGridItem(result as GridItemDataInterface)
        VisualizationStore.addGridItem(myComp as gridItemFilterInterface)
        this.updateActiveGridItem()

        this.getWidgetData(
          myComp.chartOptions,
          myComp as GridItemDataInterface,
          this.gridList.length - 1,
          true
        )
      }
    }
  }

  onDropMycomponent(event: any, chartInfo: string) {
    /* eslint-disable */
    // if (chartInfo.indexOf(GRAPH_NAME) !== -1) {
    //   this.onDropGraph(event, chartInfo)
    //   return
    // }
    // 接入geoMap
    if (chartInfo.indexOf(GEOMAP_NAME) > -1) {
      this.onDropGeoMap(event, chartInfo)
      return
    }
    if (chartInfo.indexOf('table') > -1) {
      this.onDropTable(event, chartInfo)
      return
    }
    if (chartInfo.indexOf('configuration') > -1) {
      this.onDropConfiguration(event, chartInfo)
      return
    }
    let myComp: KeyValueStore | null = null
    const chartInfoArr = chartInfo.split('$') // widgetId&dataId$chartType
    const widgetId = chartInfoArr[0] as string // id 可能为null
    const dataId = chartInfoArr[1] as string // tid
    const widgetType = chartInfoArr[3] as string // widgetType
    const find =
      widgetId === 'null'
        ? VisualizationStore.pipelineWidgetList.find(
            (pipelineListItem: any) => {
              return (
                pipelineListItem.id === null &&
                pipelineListItem.tid === Number(dataId)
              )
            }
          )
        : VisualizationStore.pipelineWidgetList.find(
            (pipelineListItem: any) => {
              return pipelineListItem.id === Number(widgetId)
            }
          )
    if (find) {
      // find.data.formData = find.data.chartOptions
      myComp = cloneDeep(find.data)
      if (myComp) {
        const offset = this.dataStore.draggingOffset
        const x =
          ((event.layerX - offset.x - 10) / VisualizationStore.scaleValue) * 100
        const y =
          ((event.layerY - offset.y) / VisualizationStore.scaleValue) * 100

        myComp.dataId = find.tid
        myComp.dataType = find.type
        myComp.widgetType = widgetType
        // myComp.formData.dataId = find.tid
        myComp.chartOptions.pipelineId = find.pipelineId
        myComp.chartOptions.size = [400, 300]

        const detailDefaultOptions = getDetailDefaultOptionsByChartType(
          myComp.chartType
        )

        myComp.chartOptions = {
          ...detailDefaultOptions,
          ...defaultChartoptions,
          ...myComp.chartOptions,
          xAxisIsShow: true, // 强制显示
          yAxisIsShow: true,
        }

        myComp.x = Math.ceil(x / this.unitX) < 0 ? 0 : Math.ceil(x / this.unitX)
        myComp.y =
          Math.floor(y / this.unitY) < 0 ? 0 : Math.floor(y / this.unitY)
        // myComp.x = Math.ceil( x / (this.unitX + this.dataStore.girdMargin[0]))
        // myComp.y = Math.ceil( y / (this.unitY + this.dataStore.girdMargin[1]))
        myComp.w = Math.ceil(400 / this.unitX) // this.dataStore.defaultGridWidth
        myComp.h = Math.ceil(300 / this.unitY) // this.dataStore.defaultGridHeight
        // myComp.formData.pipelineName = find.pipelineName
        // myComp.formData.taskName = find.taskName
        // myComp.formData.tableNameReal = find.data.formData.tableName
        // myComp.formData.dataType = ChartDataTypeEnum.myComponent
        delete myComp.chartOptions.xAxisIsShow
        delete myComp.chartOptions.axisGridIsShow
        myComp.i = `${
          chartInfo.split('|')[1] ? chartInfo.split('|')[1] : 'id'
        }${Math.random().toString(36).slice(-8)}`

        myComp.dataQuery = {
          data: {
            id: myComp.chartOptions.dataId,
            type: myComp.chartOptions.dataType,
            widgetJson: {
              ...myComp.widgetJson,
            },
          },
        }
        console.log(myComp)
        //  fix pieChart / donutChart padding issue
        if (myComp.chartType.includes('pieChart')) {
          myComp.chartOptions.padding = [10, 20, 30, 10]
        }
        VisualizationStore.addGridItem(myComp as GridItemDataInterface)
        parseChartOptionToFunction(myComp.chartOptions)
        this.updateActiveGridItem()
        this.getWidgetData(
          myComp.chartOptions,
          myComp as GridItemDataInterface,
          this.gridList.length - 1,
          true
        )
      }
    } else {
      this.$message.error('该组件不存在或已被删除')
    }
  }

  /** 筛选器组件 */
  onDropFilterForm = async (event: any) => {
    const chartType = filterFormTypeName
    const offset = this.dataStore.draggingOffset
    const x =
      ((event.layerX - offset.x - 10) / VisualizationStore.scaleValue) * 100
    const y = ((event.layerY - offset.y) / VisualizationStore.scaleValue) * 100
    const title = this.getDefaultName(chartType)
    const configuration: GridItemDataInterface = {
      chartOptions: {
        chartType: [chartType, chartType],
        title,
        startNode: '',
        filterColumns: [{ value: '', type: 'string' }],
        formData: {},
      },
      chartType: chartType,
      x: Math.ceil(x / this.unitX) < 0 ? 0 : Math.ceil(x / this.unitX),
      y: Math.floor(y / this.unitY) < 0 ? 0 : Math.floor(y / this.unitY),
      w: Math.ceil(500 / this.unitX),
      h: Math.ceil(300 / this.unitY),
      i: `filterForm${Math.random().toString(36).slice(-8)}`,
      minW: Math.ceil(288 / this.unitX),
      minH: Math.ceil(160 / this.unitY),
      resizable: true,
      moved: true,
      widgetType: 'system',
    }
    const parameters = buildSaveParameters(configuration)
    if (this.dashBoardId) {
      parameters.data.dashboardId = this.dashBoardId
    }
    const response: any = await saveWidget(parameters) // 保存configuration widget
    configuration.widgetId = response.data.result

    VisualizationStore.addGridItem(configuration)
    setTimeout(() => {
      VisualizationStore.setCurrentGridItem(
        this.gridList[this.gridList.length - 1]
      )
      this.onSave()
    }, 100)
    this.$nextTick(() => {
      const element: any = document.querySelector(
        `#grid-item-${configuration.i} filter-box`
      )
      element && (element.parentNode.style.height = 'calc(100% - 38px)')
    })
  }

  onDrop(event: any) {
    const chartMapKeys = Object.keys(chartDetailToVisClassMaps)
    if (event) {
      const chartInfo = event.dataTransfer.getData('text')
      if (chartInfo) {
        if (/\$/.test(chartInfo)) {
          this.onDropMycomponent(event, chartInfo)
        } else if (chartInfo === 'table|table') {
          this.onDropTable(event)
        } else if (chartInfo === 'text|text') {
          this.onDropText(event)
        } else if (chartInfo === 'filterForm|filterForm') {
          this.onDropFilterForm(event)
        } else if (
          chartMapKeys.some((key) =>
            chartInfo.toLocaleLowerCase().includes(key.toLocaleLowerCase())
          )
        ) {
          // 系统组件
          const offset = this.dataStore.draggingOffset
          const x =
            ((event.layerX - offset.x - 10) / VisualizationStore.scaleValue) *
            100
          const y =
            ((event.layerY - offset.y) / VisualizationStore.scaleValue) * 100

          // const formDataDefault = getDefaultFormData(chartInfo.split('|'))
          // formDataDefault.title = this.getDefaultName(chartInfo)
          // 地图组件这里需要特殊处理
          // const defaultCharConfig = // default 默认设置
          //   chartInfo.indexOf(GEOMAP_NAME) !== -1
          //     ? GeoMapDefaultConfig
          //     : defaultChartoptions

          // 默认尺寸
          const size = [400, 300]
          const chartType = chartInfo.split('|')
          const detailDefaultOptions = getDetailDefaultOptionsByChartType(
            chartType
          )
          const dataItem: GridItemDataInterface = {
            chartType,
            widgetType: 'system',
            // formData: formDataDefault,
            size,
            chartOptions: {
              ...detailDefaultOptions, // !!!!!!!! 默认设置，可能需要根据chart类型来区分，地图、graph等 !!!!!!!!!!!
              ...defaultChartoptions,
              // visibleItemsNumber: 10000000,
              // labelTextMaxlength: 10,
              title: this.getDefaultName(chartInfo),
              showChart: false,
              padding: [10, 20, 20, 10],
              value: [],
              size,
              dataType: 'dataset',
            },
            x: Math.ceil(x / this.unitX) < 0 ? 0 : Math.ceil(x / this.unitX),
            y: Math.floor(y / this.unitY) < 0 ? 0 : Math.floor(y / this.unitY),
            w: Math.ceil(400 / this.unitX),
            h: Math.ceil(300 / this.unitY),
            minW: Math.ceil(400 / this.unitX),
            minH: Math.ceil(300 / this.unitY),
            i: `${
              chartInfo.split('|')[1] ? chartInfo.split('|')[1] : 'id'
            }${Math.random().toString(36).slice(-8)}`,
            resizable: true,
            moved: true,
          }
          //  prepare for interact feat in preview mode
          dataItem.dataQuery = {
            data: {
              id: dataItem.chartOptions.dataId,
              type: dataItem.chartOptions.dataType,
              widgetJson: {
                isAggr: true,
                config: {
                  topN: 50,
                  filters: [],
                  keys: [],
                  values: [],
                },
              },
            },
          }

          // dataItem.formData.dataType = ChartDataTypeEnum.system
          if (chartInfo === 'barChart|stackBarChart') {
            dataItem.chartOptions.barIsStack = true
          }
          //  fix pieChart / donutChart padding issue
          if (chartInfo.includes('pieChart')) {
            dataItem.chartOptions.padding = [10, 20, 30, 10]
          }
          if (GRAPH_ARRAY.includes(chartType[1])) {
            dataItem.chartOptions.padding = [10, 10, 0, 0]
          }
          VisualizationStore.addGridItem(dataItem)
          this.updateActiveGridItem()
        }
      }
    }
  }

  isLayoutReady: boolean = false

  /** 改变尺寸时鼠标松开后回调，用于解决 移动尺寸后部分情况下 onGridItemResized 不触发的问题 */
  onGridCornerMouseup() {
    this.draggingItem = null
  }

  /**
   * 尺寸改变完成后
   */
  onGridItemResized(item: GridItemDataInterface, index: number) {
    this.$nextTick(() => {
      /** 销毁拖拽状态 */
      this.draggingItem = null

      const isChart = !(
        isString(item.chartType) && ['filter', 'table'].includes(item.chartType)
      )
      if (isChart) {
        const gridItem = this.gridList[index] as GridItemDataInterface
        const parentNode = this.getGridDom(item)
        if (parentNode) {
          gridItem.size = [
            Number.parseFloat(parentNode.style.width),
            Number.parseFloat(parentNode.style.height),
          ]
          this.$set(
            (this.gridList[index] as GridItemDataInterface).chartOptions,
            'size',
            [
              Number.parseFloat(parentNode.style.width),
              Number.parseFloat(parentNode.style.height),
            ]
          )
        }
        if (item.chartOptions && item.chartOptions.dataId) {
          this.$nextTick(() => {
            VisualizationStore.updateWidget({
              gridItem: item,
              index,
              syncUpdate: true,
            })
          })
        }
      } else {
        this.onSave(true)
      }
    })
  }

  onGridItemMoved(item: GridItemDataInterface, index: number) {
    const isChart = !(
      isString(item.chartType) && ['filter', 'table'].includes(item.chartType)
    )
    if (!isChart) {
      setTimeout(() => {
        this.onSave(true)
      }, 10)
    }
    if (isChart && item.formData && item.formData.dataId) {
      VisualizationStore.updateWidget({ gridItem: item, index })
    }
  }

  onGridItemResize(dataItem: GridItemDataInterface, index: number) {
    const isChart = !(
      isString(dataItem.chartType) &&
      ['filter', 'table'].includes(dataItem.chartType)
    )
    if (isChart) {
      const parentNode = this.getGridDom(dataItem)
      if (parentNode) {
        const gridItem = this.gridList[index] as GridItemDataInterface
        gridItem.size = [
          Number.parseFloat(parentNode.style.width),
          Number.parseFloat(parentNode.style.height),
        ]
        this.$set(
          (this.gridList[index] as GridItemDataInterface).chartOptions,
          'size',
          [
            Number.parseFloat(parentNode.style.width),
            Number.parseFloat(parentNode.style.height),
          ]
        )
      }
    }
  }

  getGridDom(gridItem: GridItemDataInterface) {
    const documentWin = document as any
    return documentWin.querySelector(`#grid-item-${gridItem.i}`)
  }
  /**
   * 画布grid布局渲染ready
   */
  layoutReady() {
    this.isLayoutReady = true
  }

  /**
   * layout 组件更新完成回调
   */
  layoutUpdatedEvent(/* newLayout: IGridItemType[] */) {
    this.onSave(false)
  }

  public dashBoardId: string | number | undefined = undefined

  // 若value 中存在公式，则替换为公式的值
  public transferTextValue(str: string) {
    const htmlTemplate = str

    const domparser = new DOMParser()
    const doc = domparser.parseFromString(htmlTemplate, 'text/html') // [object HTMLDocument]

    const elements = doc.querySelectorAll('.formula-style')
    if (elements.length > 0) {
      //  replace text with data-result value with innerText
      //  remove .formula-style, add result-style
      elements.forEach((item: any) => {
        item.textContent = item.getAttribute('data-result')
        item.classList.remove('formula-style')
        item.classList.add('result-style')
      })
    }

    const html = doc.documentElement.outerHTML
    const pre = html.indexOf('<body>') + '<body>'.length
    const after = html.indexOf('</body>')

    return html.slice(pre, after)
  }

  /** 保存数据 */
  onSave(noMessage: boolean = false, callback?: Function) {
    const layout: Array<any> = []
    const filters: Array<any> = []
    const tables: Array<any> = []
    const text: Array<any> = []
    this.gridList.forEach((gridItem: GridItemDataInterface) => {
      if (gridItem.chartType === 'filter') {
        const gridFilter = cloneDeep(gridItem) as gridItemFilterInterface
        const sendItem: KeyValueStore = {
          chartType: gridFilter.chartType,
          dataId: gridFilter.dataId,
          tableName: gridFilter.tableName,
          layout: gridFilter.layout,
          i: gridFilter.i,
          x: gridFilter.x,
          y: gridFilter.y,
          w: gridFilter.w,
          h: gridFilter.h,
          minW: gridFilter.minW,
          minH: gridFilter.minH,
          fieldConfig: gridFilter.fieldConfig.reduce(
            (rec: Array<any>, fieldInfo: KeyValueStore) => {
              const item = fieldInfo
              delete item.componentId
              delete item.undefined
              item.column = fieldInfo.column.value
              // item.columnType = fieldInfo.column.type
              delete item.undefined
              delete item.value
              rec.push(item)
              return rec
            },
            []
          ),
        }
        filters.push(sendItem)
      } else if (gridItem.chartType === 'table') {
        gridItem.chartOptions.sortCol = []
        tables.push({
          ...gridItem,
          chartOptions: {
            ...(gridItem.chartOptions || {}),
            value: [],
          },
        })
      } else if (gridItem.chartType === 'text') {
        // gridItem.chartOptions.value = this.transferTextValue(
        //   gridItem.chartOptions.value
        // )
        gridItem.config = []
        layout.push({
          ...gridItem,
          chartOptions: {
            ...(gridItem.chartOptions || {}),
            value: '',
          },
        })
      } else if (
        gridItem.chartType === 'filterForm' ||
        (gridItem.chartType as string[]).includes('filterForm')
      ) {
        const filterFormItem = {
          chartType: Array.isArray(gridItem.chartType)
            ? gridItem.chartType.join('|')
            : gridItem.chartType,
          // chartOptions: cloneDeep(gridItem.chartOptions),
          x: gridItem.x,
          y: gridItem.y,
          w: gridItem.w,
          h: gridItem.h,
          widgetType: gridItem.widgetType,
          minW: gridItem.minW,
          minH: gridItem.minH,
          i: gridItem.i,
          widgetId: gridItem.widgetId,
        }
        layout.push(filterFormItem)
      } else {
        layout.push({
          chartType: Array.isArray(gridItem.chartType)
            ? gridItem.chartType.join('|')
            : gridItem.chartType,
          x: gridItem.x,
          y: gridItem.y,
          w: gridItem.w,
          h: gridItem.h,
          widgetType: gridItem.widgetType,
          minW: gridItem.minW,
          minH: gridItem.minH,
          i: gridItem.i,
          widgetId: gridItem.widgetId,
        })
      }
    })
    let apiQuery = saveDashboard
    const parameters: KeyValueStore = {
      layout: JSON.stringify({
        w: VisualizationStore.layoutData.width,
        h: VisualizationStore.layoutData.height,
        background: VisualizationStore.layoutData.background,
        scale: VisualizationStore.scaleValue,
        filters,
        tables,
        text,
        gridItems: layout,
        coordinations: VisualizationStore.coordinations,
      }),
      projectId: this.projectId,
      status: 0,
    }
    if (this.dashBoardId) {
      parameters.id = this.dashBoardId
      apiQuery = updateDashboard
    }
    /**
     * 同步widget信息，保证每次dashboard与widget内容同步
     */
    // VisualizationStore.gridList.forEach((gridItem,index) => {
    //   VisualizationStore.updateWidget({ gridItem, index })
    // })
    apiQuery({ data: { ...parameters } }).then((response) => {
      if (!noMessage) {
        this.$message.success('保存成功')
      }
      if (response.data.code === 100 && response.data.result) {
        this.dashBoardId = response.data.result
        VisualizationStore.setCurrentDashboardData({ id: response.data.result })
      }
      if (callback) {
        callback()
      }
    })
  }

  flexLayout() {
    ;(this.$refs.gridLayout as any).flexLayout(() => {
      this.dataStore.gridList.forEach(
        (gridItem: GridItemDataInterface, index: number) => {
          this.onGridItemResized(gridItem, index)
        }
      )
      this.$nextTick(() => {
        this.onSave()
      })
    })
  }

  syncFilterHeight(gridItem: gridItemFilterInterface, callback?: Function) {
    setTimeout(() => {
      const box = this.$refs[`control-list-${gridItem.i}`]
      if (box && (box as Element[])[0]) {
        this.$set(
          gridItem,
          'h',
          (((box as Element[])[0] as HTMLElement).offsetHeight + 16) /
            this.unitY
        )
        // 模拟添加一个无效的touch grid
        this.touchGridList()
        setTimeout(() => {
          if (gridItem.layout === controlLayoutEnum.horizontal) {
            this.flexLayout()
            if (callback) {
              callback()
            }
          } else if (callback) {
            callback()
          }
        }, 10)
      } else if (callback) {
        callback()
      }
    }, 10)
  }

  syncChartData(gridItemFilter: gridItemFilterInterface) {
    const dataId = gridItemFilter.dataId as string
    if (!dataId) {
      return
    }
    VisualizationStore.gridList.forEach(
      (gridItem: GridItemDataInterface, index: number) => {
        if (
          (gridItem.chartType === 'table' ||
            (gridItem.chartType !== 'filter' && gridItem.widgetId) ||
            !checkIsFilterForm(gridItem)) &&
          gridItem.formData &&
          gridItem.formData.dataId === dataId
        ) {
          this.getWidgetData(
            gridItem.formData as KeyValueStore,
            gridItem,
            index
          )
        }
      }
    )
  }

  onDialogControllerSettingHandle(data: KeyValueStore) {
    if (data.status) {
      if (data.editGridItem) {
        // 编辑模式
        const editGridItem = this.dataStore.gridList.find(
          (item) => item.i === data.editGridItem.i
        )
        if (editGridItem) {
          const fieldConfig: Array<ControlConfigurationInterface> = []
          data.emitor.fieldList.forEach((fieldInfo: KeyValueStore) => {
            const controllerRender = getControllerRenderByFormData(
              fieldInfo.formConfig.formData
            ) as componentTypeEnum
            const defaultValue = getDefaultValue(controllerRender, fieldInfo)
            const formData = fieldInfo.formConfig
              .formData as ControlConfigurationInterface
            formData.controlRender = controllerRender

            const controlNew: ControlConfigurationInterface = {
              ...formData,
              value: defaultValue || '',
              column: cloneDeep(fieldInfo.column),
              columnType: fieldInfo.column.type,
              options: cloneDeep(fieldInfo.options),
            }
            fieldConfig.push(controlNew)
          })
          this.$set(editGridItem, 'fieldConfig', fieldConfig)
          this.syncFilterHeight(editGridItem as gridItemFilterInterface, () => {
            const dialog = this.$refs['dialog-contrller-setting'] as any
            if (dialog) {
              dialog.clear()
            }
            /**
             这里需要将取消关联的表进行刷新
             */
            const lastBindIds = this.lastBindIds as KeyValueStore
            const currentBindIds = Object.values(
              this.getBindChartIds(editGridItem as gridItemFilterInterface)
            ) as Array<string>
            const needRefreshIds = [] as Array<string>
            Object.values(lastBindIds).forEach((id: string) => {
              if (!currentBindIds.includes(id)) {
                needRefreshIds.push(id)
              }
            })
            if (needRefreshIds.length > 0) {
              VisualizationStore.gridList.forEach(
                (refreshItem, gridItemindex) => {
                  if (needRefreshIds.includes(refreshItem.i as string)) {
                    if (
                      (refreshItem.chartType === 'table' ||
                        refreshItem.widgetId) &&
                      refreshItem.formData &&
                      refreshItem.formData.dataId
                    ) {
                      this.getWidgetData(
                        refreshItem.formData as KeyValueStore,
                        refreshItem,
                        gridItemindex
                      )
                    }
                  }
                }
              )
            }
            this.syncChartData(editGridItem as gridItemFilterInterface)
            this.$set(this.dialogControllerSettingConfig, 'visible', false)
          })
        }
      } else {
        const yPos = this.getControllerYPos()
        const gridItemNew: gridItemFilterInterface = {
          chartType: 'filter',
          tableName: data.tableName,
          dataId: data.dataId,
          layout:
            this.dialogControllerSettingConfig.layout ||
            controlLayoutEnum.vertical,
          fieldConfig: [] as Array<ControlConfigurationInterface>,
          chartOptions: {},
          x: 0,
          y: yPos,
          w: Math.round(this.dataStore.layoutData.width / this.unitX),
          h: 8,
          i: `grid-item-filter-${Math.random().toString(36).slice(-8)}`,
        }
        data.emitor.fieldList.forEach((fieldInfo: KeyValueStore) => {
          const controllerRender = getControllerRenderByFormData(
            fieldInfo.formConfig.formData
          ) as componentTypeEnum
          const defaultValue = getDefaultValue(controllerRender, fieldInfo)
          const formData = fieldInfo.formConfig
            .formData as ControlConfigurationInterface
          formData.controlRender = controllerRender
          const controlNew: ControlConfigurationInterface = {
            ...formData,
            value: defaultValue || '',
            column: cloneDeep(fieldInfo.column),
            columnType: fieldInfo.column.type,
            options: cloneDeep(fieldInfo.options),
          }
          gridItemNew.fieldConfig.push(controlNew)
        })
        this.dataStore.gridList.push(gridItemNew)
        this.syncFilterHeight(gridItemNew, () => {
          const dialog = this.$refs['dialog-contrller-setting'] as any
          if (dialog) {
            dialog.clear()
          }
          this.$set(this.dialogControllerSettingConfig, 'visible', false)
          this.syncChartData(gridItemNew)
        })
      }
    } else {
      this.$set(this.dialogControllerSettingConfig, 'visible', false)
    }
  }
  getControllerYPos() {
    const controls = this.gridList.filter((item) => item.chartType === 'filter')
    if (controls && controls.length > 0) {
      let lastY = 0
      controls.forEach((item) => {
        if ((item.y as number) + (item.h as number) > lastY) {
          lastY = (item.y as number) + (item.h as number)
        }
      })
      return lastY
    }
    return 0
  }

  onTablePageChange(
    gridItem: GridItemDataInterface,
    pageConfig: { pageNumber: number; pageSize: number }
  ) {
    this.$set(gridItem.chartOptions, 'curPage', pageConfig.pageNumber)
    this.$set(gridItem.chartOptions, 'pageSize', pageConfig.pageSize)

    this.getTableData(gridItem /* , [] */)
  }

  /**
   * 排序
   */
  private onTableSortchange(
    gridItem: GridItemDataInterface,
    index: number,
    sortData: { key: string; sort: 'desc' | 'asc' }
  ) {
    if (sortData.key) {
      this.$set(gridItem.chartOptions, 'sortCol', [
        { name: sortData.key, sort: sortData.sort },
      ])
      this.getTableData(gridItem /* , [] */)
    }
  }

  /**
   * 保存控件参数
   */
  private async saveConfigurationParams(gridItem: GridItemDataInterface) {
    if (this.configurationExecuteId) {
      return
    }
    const { pipelineId, dataId, params, value = {} } = gridItem.chartOptions
    const response: any = await simpleUpdate({
      data: {
        ...gridItem.chartOptions.info,
        data: {
          algType: gridItem.chartOptions.info.algType,
          setParams: params.map((p: KeyValueStore) => {
            let newValue = p.value
            if (value[p.name] !== undefined) {
              newValue = p.type === 'int' ? +value[p.name] : value[p.name]
            }

            return {
              ...p,
              value: newValue,
            }
          }),
        },
        id: dataId,
        pipelineId,
        projectId: this.projectId,
      },
    })
    const parameters = buildSaveParameters(gridItem)
    if (this.dashBoardId) {
      parameters.data.dashboardId = this.dashBoardId
    }
    if (gridItem.widgetId) {
      await updateWidget(parameters) // 保存configuration widget
    }

    if (response.data.code === 100) {
      this.executeConfiguration(gridItem) // 执行
    } else {
      const { message, tips } = response.data
      this.$message.error(message || tips)
    }
  }

  /**
   * 停止执行
   */
  private async stopConfigurationExecute(gridItem: GridItemDataInterface) {
    this.configurationExecuteId = ''
    const response = await pipelineStop({
      data: {
        projectId: this.projectId,
        sessionId: this.configurationExecuteSessionId,
      },
    })
    if (response.data.code === 100) {
      this.$message.success('停止成功')
    } else {
      this.$message.error(response.data.message || '停止失败，请稍候再试')
    }
  }

  /**
   * 执行控件
   */
  private async executeConfiguration(gridItem: GridItemDataInterface) {
    const { pipelineId, taskId } = gridItem.chartOptions
    this.configurationExecuteId = gridItem.widgetId
    const response: any = await pipelineExecute({
      data: {
        id: pipelineId,
        taskId,
        projectId: this.projectId,
        sampling: false,
      },
    })
    if (response.data.code === 100) {
      const [sessionId] = response.data.result
      this.configurationExecuteSessionId = sessionId
      this.$socket.emit('queryStatus', { pipelineId, sessionId })
    }
  }

  /**
   * 初始化监听控件的运行结果
   */
  private initConfigurationWS() {
    this.$socket.on('queryStatus', this.parseConfigurationWS)
  }

  /**
   * 解析控件执行结果
   */
  private parseConfigurationWS(data: any) {
    console.log('parseConfigurationWS', data)
    if (!this.configurationExecuteId) {
      return
    }
    const pipelineId = this.gridList.filter(
      (list) => list.widgetId === this.configurationExecuteId
    )[0].chartOptions.pipelineId
    if (data.status === 'SUCCESS') {
      this.$message.info('控件执行成功，正在更新相关图表数据', 4)
      if (pipelineId) {
        //  update for formulaList result
        VisualizationStore.fetchFormulaList({
          projectId: Number(this.$route.params.id),
        })

        this.gridList.forEach((gridItem, index) => {
          if (
            gridItem.widgetId !== this.configurationExecuteId &&
            gridItem.chartOptions.pipelineId === pipelineId && // pipeline 下的其他chart获取数据
            gridItem.chartType !== 'configuration'
          ) {
            this.getWidgetData(
              gridItem.chartOptions as KeyValueStore,
              gridItem,
              index
            )
          }
        })
        setTimeout(() => {
          this.saveAllTextValue()
        }, 500)
      }
    } else {
      this.$message.error('控件执行失败，请检查配置稍候再试', 4)
    }
    this.configurationExecuteId = ''
  }

  beforeDestroy() {
    this.dataStore.setActiveSettingItem('background')
    // 退出之前销毁缓存，防止节点数据改变后查询数据不变
    visGraphService.clearCache()
    this.dataStore.setPipelineId('')
    this.dataStore.setDataset([])
    this.$socket.off('queryStatus', this.parseConfigurationWS)
  }

  private async queryNewPipelineWidgetList(newPipelineId: string) {
    // 查询复制pipeline下的widget信息，主要获取Configuration的新配置
    const response: any = await widgetQueryByProject({
      data: {
        projectId: this.projectId,
        pipelineId: newPipelineId,
      },
    })
    if (response.data.code === 100) {
      return response.data.result[1].taskWidgets || []
    }
    return []
  }

  /** 组件开始拖拽 */
  public onGridItemDragStart() {
    this.draggingFlag = true
  }

  /** 组件拖拽结束 */
  public onGridItemDragEnd() {
    this.draggingFlag = false
    this.draggingItem = null
  }

  /** 移动过程中检测 */
  public onGridItemResizeMove = (grid: any) => {
    this.checkDragingLine(grid, true)
  }

  /** 根据GridItem实力 */
  private getDomRect(gridCompInstance: any): DomReact {
    const [x = 0, y = 0] = (gridCompInstance.style.transform
      .replace('translate3d(', '')
      .split(',') as any[])
      .filter((_, index) => {
        return index < 2
      })
      .map((item) => {
        return parseInt(item)
      })
    return {
      x,
      y,
      width: parseInt(gridCompInstance.style.width),
      height: parseInt(gridCompInstance.style.height),
    }
  }

  /** 参考线检测 */
  private checkDragingLine(grid: any, checkFlag?: boolean) {
    // 先判断，可以在只剩下一个组件的情况下提升性能
    if ((this.$refs.gridItems as any[])?.length < 2) {
      return
    }
    this.draggingItem = grid
    const [x = 0, y = 0] = (grid.style.transform
      .replace('translate3d(', '')
      .split(',') as any[])
      .filter((_, index) => {
        return index < 2
      })
      .map((item) => {
        return parseInt(item)
      })

    this.draggingDomRect = {
      x,
      y,
      width: parseInt(grid.style.width),
      height: parseInt(grid.style.height),
    }
    this.checkAlignMent(checkFlag)
  }

  /** 组件拖拽过程检测 */
  public onGridItemDragMove(grid: any) {
    this.checkDragingLine(grid)
  }

  /** 碰撞检测 */
  private findTag(
    ohterReactItems: any[],
    attrOhterSort: string,
    attrOffset: string,
    maxOffset: number = 5
  ) {
    return ohterReactItems
      .sort((a, b) => {
        return a[attrOhterSort] - b[attrOhterSort]
      })
      .find((item) => {
        return item[attrOffset] < maxOffset
      })
  }

  /** 获取横向参考线样式 */
  private getHlineStyle(
    findData: DomReact,
    draggingData: DomReact,
    ohterGridDomReact: DomReact[],
    tagAttr: string
  ) {
    const matchItems = ohterGridDomReact.filter(
      (item) => item[tagAttr] == findData[tagAttr]
    )
    matchItems.push(draggingData)
    const minX = matchItems.sort((a, b) => {
      return a.x - b.x
    })[0].x
    const maxXItem = matchItems.sort((a, b) => {
      return b.x - a.x
    })[0]
    const maxX = maxXItem.x + maxXItem.width
    return {
      width: `${maxX - minX}px`,
      transform: `translateX(${minX}px)`,
    }
  }

  /** 获取纵向参考线样式 */
  private getVlineStyle(
    findData: DomReact,
    draggingData: DomReact,
    ohterGridDomReact: DomReact[],
    tagAttr: string
  ) {
    const matchItems = ohterGridDomReact.filter(
      (item) => item[tagAttr] == findData[tagAttr]
    )
    matchItems.push(draggingData)
    const minY = matchItems.sort((a, b) => {
      return a.y - b.y
    })[0].y
    const maxYItem = matchItems.sort((a, b) => {
      return b.y - a.y
    })[0]
    const maxY = maxYItem.y + maxYItem.height
    return {
      height: `${maxY - minY}px`,
      transform: `translateY(${minY}px)`,
    }
  }

  private checkAlignMent(noCheckFlag?: boolean) {
    const compItems = this.$refs.gridItems
    const { draggingItem, draggingDomRect } = this
    const flag = noCheckFlag ? true : this.draggingFlag
    if (
      flag &&
      draggingItem &&
      draggingDomRect &&
      (compItems as any[])?.length > 1
    ) {
      const otherGridItems = (compItems as any[]).filter(
        (item) => item.$attrs.id !== this.draggingItem?.$attrs.id
      )
      /** 计算出拖拽grid与其他grid的offset数据以及预设定位数据 */
      const ohterGridDomReact: DomReact[] = otherGridItems.map((item) => {
        const itemRect = this.getDomRect(item)
        return {
          ...itemRect,
          ht: itemRect.y,
          hc: itemRect.y + itemRect.height / 2,
          hb: itemRect.y + itemRect.height,
          vl: itemRect.x,
          vc: itemRect.x + itemRect.width / 2,
          vr: itemRect.x + itemRect.width,
          offsetHt: Math.abs(itemRect.y - draggingDomRect.y),
          offsetHc: Math.abs(
            itemRect.y +
              itemRect.height / 2 -
              (draggingDomRect.y + draggingDomRect.height / 2)
          ),
          offsetHb: Math.abs(
            itemRect.y +
              itemRect.height -
              (draggingDomRect.y + draggingDomRect.height)
          ),
          offsetVl: Math.abs(itemRect.x - draggingDomRect.x),
          offsetVc: Math.abs(
            itemRect.x +
              itemRect.width / 2 -
              (draggingDomRect.x + draggingDomRect.width / 2)
          ),
          offsetVr: Math.abs(
            itemRect.x +
              itemRect.width -
              (draggingDomRect.x + draggingDomRect.width)
          ),
        }
      })

      /** 检测水平头部是否达到吸附条件 */
      const indexFindHt = this.findTag(
        ohterGridDomReact,
        'offsetHt',
        'offsetHt',
        4
      )
      if (indexFindHt) {
        this.htStyle = this.getHlineStyle(
          indexFindHt,
          draggingDomRect,
          ohterGridDomReact,
          'offsetHt'
        )
        this.ht = indexFindHt.ht
        this.alignmentFlagHT = true
      } else {
        this.alignmentFlagHT = false
      }

      /** 检测水平中部是否达到吸附条件 */
      const indexFindHc = this.findTag(
        ohterGridDomReact,
        'offsetHc',
        'offsetHc',
        4
      )
      if (indexFindHc) {
        this.hcStyle = this.getHlineStyle(
          indexFindHc,
          draggingDomRect,
          ohterGridDomReact,
          'offsetHc'
        )
        this.hc = indexFindHc.hc
        this.alignmentFlagHC = true
      } else {
        this.alignmentFlagHC = false
      }

      /** 检测水平底部是否达到吸附条件 */
      const indexFindHb = this.findTag(
        ohterGridDomReact,
        'offsetHb',
        'offsetHb',
        4
      )
      if (indexFindHb) {
        this.hbStyle = this.getHlineStyle(
          indexFindHb,
          draggingDomRect,
          ohterGridDomReact,
          'offsetHb'
        )
        this.hb = indexFindHb.hb
        this.alignmentFlagHB = true
      } else {
        this.alignmentFlagHB = false
      }

      /** 检测垂直左侧是否达到吸附条件 */
      const indexFindVl = this.findTag(
        ohterGridDomReact,
        'offsetVl',
        'offsetVl',
        4
      )
      if (indexFindVl) {
        this.vlStyle = this.getVlineStyle(
          indexFindVl,
          draggingDomRect,
          ohterGridDomReact,
          'offsetVl'
        )
        this.vl = indexFindVl.vl
        this.alignmentFlagVL = true
      } else {
        this.alignmentFlagVL = false
      }
      /** 检测垂直中部是否达到吸附条件 */
      const indexFindVc = this.findTag(
        ohterGridDomReact,
        'offsetVc',
        'offsetVc',
        4
      )
      if (indexFindVc) {
        this.vcStyle = this.getVlineStyle(
          indexFindVc,
          draggingDomRect,
          ohterGridDomReact,
          'offsetVc'
        )
        this.vc = indexFindVc.vc
        this.alignmentFlagVC = true
      } else {
        this.alignmentFlagVC = false
      }

      /** 检测垂直中部是否达到吸附条件 */
      const indexFindVr = this.findTag(
        ohterGridDomReact,
        'offsetVr',
        'offsetVr',
        4
      )
      if (indexFindVr) {
        this.vrStyle = this.getVlineStyle(
          indexFindVr,
          draggingDomRect,
          ohterGridDomReact,
          'offsetVr'
        )
        this.vr = indexFindVr.vr
        this.alignmentFlagVR = true
      } else {
        this.alignmentFlagVR = false
      }
    }
  }

  /** 检测是否是筛选组件 */
  checkIsFilterForm(gridItem: GridItemDataInterface) {
    return checkIsFilterForm(gridItem)
  }

  /** 获取gridIndex */
  getGridIndex(attrCompare: string | number, attr: string = 'i') {
    return this.gridList.findIndex((item: any) => item[attr] === attrCompare)
  }

  /** 筛选控件中的筛选条件改变 */
  filterChange(data: any, gridItem: GridItemDataInterface) {
    const index = this.getGridIndex(gridItem.i, 'i')
    const chartOptions = { ...gridItem.chartOptions, formData: {} }
    VisualizationStore.updateWidget({
      gridItem: {
        ...gridItem,
        chartOptions,
      },
      index,
    })
  }

  /** 获取hover状态下的filter-form的关联图表 */
  get filterAssociationChartHover() {
    let result = [] as Array<string | number>
    if (this.hoverFilterFormInfo) {
      const currentGrid = VisualizationStore.gridList.find(
        (item) => item.i === this.hoverFilterFormInfo?.i
      )
      result = currentGrid?.chartOptions?.associationChart || []
    }
    return result
  }

  /** 鼠标移入filter-form */
  handleFilterFormMouseEnter(gridItem: GridItemDataInterface) {
    this.hoverFilterFormInfo = {
      i: gridItem.i as any,
      widgetId: gridItem.widgetId,
    }
  }

  /** 鼠标移出fitler-form */
  handleFilterFormMouseLeave(gridItem: GridItemDataInterface) {
    this.hoverFilterFormInfo = null
  }
  /** 筛选器运行状态检测对象 */
  loadingMaps: any = {}

  /** 判断筛选执行情况 */
  checkIsRunning(gridItem: GridItemDataInterface) {
    return (
      this.loadingMaps[`widget_${gridItem.widgetId}`] &&
      this.loadingMaps[`widget_${gridItem.widgetId}`].length > 0
    )
  }

  /** 加载关联图表chart */
  loadAssociationCharts(gridItem: GridItemDataInterface) {
    const associationCharts = ((gridItem.chartOptions?.associationChart ||
      []) as Array<string | number>).filter((id) => {
      return (
        VisualizationStore.gridList.findIndex((grid) => grid.widgetId === id) >
        -1
      )
    })
    if (associationCharts.length === 0) {
      message.info('筛选器没有有效关联组件')
      this.filterFormExecuteId = ''
      return
    }
    VisualizationStore.setIsSettingPanelChangeLock(true)
    this.filterFormExecuteId = gridItem.widgetId
    this.$set(
      this.loadingMaps,
      `widget_${gridItem.widgetId}`,
      cloneDeep(associationCharts)
    )
    const filterFormWidgetJSON = getFilterDataFromFilterForm(gridItem)
    associationCharts.forEach((wegetId) => {
      const asGridItem = this.dataStore.gridList.find((item) => {
        return item.widgetId === wegetId
      })
      if (asGridItem) {
        this.$set(asGridItem, 'filterFormJson', filterFormWidgetJSON)
        this.getWidgetData(
          asGridItem.chartOptions,
          asGridItem,
          this.getGridIndex(asGridItem.widgetId, 'widgetId'),
          false,
          true
        )
      }
    })
  }

  /** 执行或者停止筛选 */
  filterFormExecute(isStart: boolean, gridItem: GridItemDataInterface) {
    VisualizationStore.setCurrentGridItem(gridItem)
    this.$nextTick(() => {
      if (isStart) {
        this.loadAssociationCharts(gridItem)
      } else {
        this.filterFormExecuteId = ''
        this.$set(this.loadingMaps, `widget_${gridItem.widgetId}`, [])
      }
    })
  }

  /** 重置 */
  filterFormReset(gridItem: GridItemDataInterface) {
    const index = this.getGridIndex(gridItem.i, 'i')
    const chartOptions = gridItem.chartOptions
    let queryData = {} as any
    const FilterForm = this.$refs[`filter-form-${gridItem.i}`] || []
    const that = this

    if (FilterForm && (FilterForm as any)[0]) {
      queryData = (FilterForm as any)[0].getResetData() || {}
    }
    VisualizationStore.updateWidget({
      gridItem: {
        ...gridItem,
        chartOptions: {
          ...chartOptions,
          queryData,
        },
      },
      index,
      onSuccess(gridItemSaved: gridItemFilterInterface) {
        that.loadAssociationCharts(gridItemSaved)
      },
    })
  }
  /**
   * chart触发的事件
   */
  private onChartEvent(
    { name, value }: { name: string; value: any },
    gridItem: GridItemDataInterface,
    index: number
  ) {
    switch (name) {
      case 'lineGraphNodeDragend': // graph-linechart 拖动节点位置，需要记住位置
        this.saveGraphNodePosition(value, gridItem, index)
        break
      case 'graphNodeClick': // graph node 点击
        this.onGraphNodeClick(value, gridItem, index)
        break
      default:
        break
    }
  }

  /**
   * 保存graph node 节点位置
   */
  private saveGraphNodePosition(
    position: { id: string; x: number; y: number }[],
    gridItem: GridItemDataInterface,
    index: number
  ) {
    this.$set(
      (this.gridList[index] as GridItemDataInterface).chartOptions,
      'nodePosition',
      position
    )
    gridItem.chartOptions.value.nodes.forEach((node: any) => {
      const targetPosition = position.filter((p) => p.id === node.id)[0]
      if (targetPosition) {
        node.x = targetPosition.x
        node.y = targetPosition.y
      }
    })
    VisualizationStore.updateWidget({ gridItem, index })
  }

  private onGraphNodeClick(
    value: any,
    gridItem: GridItemDataInterface,
    index: number
  ) {
    console.log(value, gridItem, index)
  }

  private getBoxHeightStyle(item: GridItemDataInterface) {
    if (
      !item.showChart &&
      item.chartOptions &&
      item.chartType !== 'filter' &&
      item.chartOptions.title
    ) {
      return {
        height: 'calc(100% - 38px)',
      }
    }
    return {
      height: '100%',
    }
  }

  get pipelineId() {
    return this.dataStore.pipelineId
  }
   

  get dataset(): any[] {
    return this.dataStore.dataset
  }

  public async datasetList() {
    const postData = {
      projectId: this.$route.params.id,
    }
    const response = await queryDatalist2({ data: postData })
    if (response.data.code === 100) {
      this.dataStore.setDataset((response.data.result || []) as any[])
    } else {
      this.$message.error('获取数据集失败')
    }
    this.inited = true
  }
}
</script>
<style lang="less" scoped>
.grid-layout-design {
  height: 100%;
  position: relative;
  width: 100%;

  .drag-marker {
    background: rgba(255, 255, 255, 0.1);
    margin: 4px 8px;
    position: absolute;
    right: 0;
    top: 0;
    z-index: 1009;

    &:hover,
    &.is-loading {
      background: rgba(255, 255, 255, 0.4);
    }
  }

  .line {
    position: absolute;

    &.ht,
    &.hc,
    &.hb {
      background: #e6505a;
      height: 1px;
      left: 0;
      position: absolute;
      top: 50%;
      width: 100%;
      z-index: 10000;
    }

    &.vl,
    &.vc,
    &.vr {
      background: #e6505a;
      height: 100%;
      left: 50%;
      position: absolute;
      top: 0;
      width: 1px;
      z-index: 10000;
    }
  }

  .progress-bar {
    animation: gradient 1.5s ease infinite;
    background: linear-gradient(
      90deg,
      #c0bfff78 25%,
      #5760e6 37%,
      #c0bfff78 63%
    );
    background-size: 400% 100%;
    height: 3px;
    position: absolute;
    top: 0;
    width: 100%;
    z-index: 999;
  }

  .text-editor {
    display: flex;
    flex-direction: column-reverse;
    height: 100%;
    padding: 0 15px;
    position: relative;
    // z-index: 2010;
    /deep/ .editor-box {
      margin-top: 0;
    }
  }

  .no-data {
    align-content: center;
    align-items: center;
    background: transparent;
    display: flex;
    height: 100%;
    justify-content: center;
    text-align: center;
    transform: translateY(-40px);
    width: 100%;
    z-index: 100;

    .loading-status-img,
    .no-data-img {
      display: block;
      height: auto;
      max-width: 75%;
      width: 208px;
    }

    .no-data-block {
      align-items: center;
      display: flex;
      flex-flow: column;
      justify-content: center;
    }

    .no-data-text {
      color: #d9d9d9;
    }
  }

  .id-box {
    background: #5760e6;
    color: #fff;
    display: inline-block;
    font-size: 12px;
    height: 16px;
    left: 0;
    line-height: 16px;
    max-width: calc(100% - 16px);
    padding: 0 4px;
    position: absolute;
    top: 0;
    z-index: 999;
  }

  .grid-item {
    background-color: #fff;
    border: 1px solid #e9e9e9;
    border-radius: 4px !important;
    box-sizing: border-box;
    overflow: hidden;
    position: relative;

    &.active,
    &:hover {
      border: 1px solid #c5c9ff;
    }

    // .grid-item-close {
    //   transform: translateY(4px);
    // }

    .grid-item-close,
    .grid-item-edit,
    .grid-item-execute {
      cursor: pointer;
      display: inline-block;
      font-size: 16px;
      line-height: 32px;
      margin-right: 8px;
      opacity: 0;
      pointer-events: none;

      &.disable {
        cursor: not-allowed;
        opacity: 0.5 !important;
      }

      img {
        height: auto;
        width: 24px;
      }
    }

    .grid-item-drag-handler {
      height: 20px;
      left: 0;
      position: absolute;
      right: 55px;
      top: 0;
      z-index: 9;
    }

    &:hover {
      .grid-item-close,
      .grid-item-edit,
      .grid-item-execute,
      /deep/ .comp-filter-form-toolbar {
        opacity: 1;
        pointer-events: all;
      }
    }

    .loading-status {
      align-content: center;
      align-items: center;
      bottom: 0;
      color: #222432;
      display: flex;
      flex-flow: row nowrap;
      font-size: 12px;
      font-weight: 500;
      justify-content: center;
      left: 0;
      letter-spacing: 1px;
      line-height: 36px;
      position: absolute;
      right: 0;
      top: 0;
      vertical-align: middle;
      z-index: 1000;

      .loading-text {
        display: inline-block;
        padding-left: 4px;
      }

      .sync-icon {
        color: #5760e6;
        font-size: 24px;
      }
    }
  }

  .title-box {
    color: #5d637e;
    font-size: 14px;
    font-weight: 500;
    height: 38px;
    letter-spacing: 1px;
    line-height: 38px;
    overflow: hidden;
    padding: 0 16px;
    padding-right: 55px;
    text-overflow: ellipsis;
    white-space: nowrap;
    width: 100%;
  }

  .no-title.title-box {
    display: none;
  }

  /deep/ .vue-grid-item.vue-grid-placeholder {
    background-color: #f9f9fc;
    opacity: 1;
  }

  /deep/ .vue-resizable-handle {
    z-index: 2000;
  }

  .control-box-list {
    box-sizing: border-box;
    padding-bottom: 0;
    padding-top: 14px;

    &.list-vertical {
      padding-bottom: 2px;
    }
  }
}

/deep/ .formula-style {
  background-color: #e1e3fe;
  color: #6973ff;
}

/deep/ .result-style {
  color: #6973ff;
}

/deep/ .tox-tinymce.tox-tinymce-inline {
  float: left;
  opacity: 0;
  width: 587px;
}

/deep/ .chart-title {
  color: #5d637e !important;
  font-size: 14px !important;
  font-weight: 500;
  height: 38px;
  line-height: 38px;
  padding: 0 16px;
  text-align: left !important;
}

.error-border {
  border: 1px solid #e6505a !important;
}
@keyframes gradient {
  0% {
    background-position: 100% 50%;
  }

  100% {
    background-position: 0% 50%;
  }
}

.filter-form-hander {
  background: transparent;
  box-sizing: border-box;
  height: 100%;
  left: 0;
  padding-top: 24px;
  position: absolute;
  top: 0;
  width: 100%;
  z-index: 8;
}
</style>
